Skip to main content

Barbara API SDK for Python

Official Python SDK for the Barbara Edge AI platform API.

Typed, synchronous and asynchronous clients for managing nodes, clusters, applications, models, and related resources.

Full documentation

CI Security Docs PyPI version Python versions License: MIT Typed


Overview

The Barbara API covers the full platform: nodes, clusters, applications, models, and everything needed to configure them, over plain HTTP. This SDK wraps it in a typed client, so you get autocomplete, structured response objects, and automatic token refresh instead of raw requests calls and hand-rolled JSON payloads. A synchronous client and an asyncio-native client expose the same methods, so you can start with BarbaraClient and move to AsyncBarbaraClient later without relearning the API.

Contents: Requirements · Installation · Authentication · Quick start · Usage · Error handling · Architecture · API reference · Examples · Roadmap · Contributing · License

Requirements

  • Python 3.9 or later

Installation

pip install barbara-api-sdk

Authentication

The SDK authenticates against the Barbara API using OAuth2 password grant. You need four credentials, referred to as Barbara API Credentials:

Credential Description
BBR_API_USERNAME Your Barbara Panel username
BBR_API_PASSWORD Your Barbara Panel password
BBR_API_CLIENT_ID OAuth2 client ID, provided by Barbara
BBR_API_CLIENT_SECRET OAuth2 client secret, provided by Barbara

[!NOTE] Panel credentials can be created at onboarding.barbara.tech. Client credentials are issued by Barbara support.

By default, the client reads credentials from environment variables:

export BBR_API_USERNAME="..."
export BBR_API_PASSWORD="..."
export BBR_API_CLIENT_ID="..."
export BBR_API_CLIENT_SECRET="..."
from barbara import BarbaraClient

client = BarbaraClient.from_env()

Additional optional environment variables:

Variable Description Default
BBR_API_URL Barbara API base URL https://prod.bap.barbara.tech
BBR_AUTH_URL Barbara auth server base URL https://prod.auth.barbara.tech/auth
BBR_REALM Authentication realm bbr_prod

Credentials can also be supplied explicitly instead of through environment variables:

from barbara import BarbaraClient, BarbaraConfig

config = BarbaraConfig(
    client_id="...",
    client_secret="...",
    username="...",
    password="...",
)
client = BarbaraClient(config)

Quick start

from barbara import BarbaraClient

with BarbaraClient.from_env() as client:
    for node in client.nodes.list():
        print(node.node_name, node.status)

    node = client.nodes.resolve("my-node-01")

Async usage

AsyncBarbaraClient mirrors BarbaraClient method for method: only await differs.

import asyncio
from barbara import AsyncBarbaraClient

async def main():
    async with AsyncBarbaraClient.from_env() as client:
        nodes = await client.nodes.list()

asyncio.run(main())

Usage

Nodes

See Node management in Academy.

nodes = client.nodes.list(search="sensor")
node = client.nodes.get("<node-id>")
node = client.nodes.resolve("my-node-01")  # look up by node name
nodes = client.nodes.list_by_tags(["production", "line-3"])  # any of the given tags

client.nodes.reboot("<node-id>")
client.nodes.poweroff("<node-id>")

Node global secrets

Barbara-managed secrets shared by every workload on the node — distinct from a Marketplace app's own App Secrets and from Docker-native Swarm Secrets. See Secrets in Academy.

client.nodes.create_global_secrets("<node-id>", {"wifi-psk": "s3cr3t"})
secrets = client.nodes.list_global_secrets("<node-id>")
client.nodes.delete_global_secret("<node-id>", "<secret-id>")

Node global configuration

Corresponds to the Panel's Global Config (node-scoped; see Application configuration types for the full picture — a workload's own App Config is set through client.nodes.workloads, below).

client.nodes.set_global_config("<node-id>", config={"threshold": 5})
config = client.nodes.get_global_config("<node-id>")

Docker credentials

client.nodes.create_docker_credentials(
    "<node-id>", [{"user": "bob", "password": "s3cr3t", "server": "docker.io"}]
)

creds = client.nodes.list_docker_credentials("<node-id>")
print(creds[0].server, creds[0].user)  # password is never returned by the API

Node identity: name, tags, location, safety actions

See General info in Academy.

client.nodes.update_name("<node-id>", "floor-2-sensor-01")
client.nodes.add_tag("<node-id>", "production")

client.nodes.set_location("<node-id>", lat=40.4168, lng=-3.7038, city="Madrid")
location = client.nodes.get_location("<node-id>")

client.nodes.update_safety_actions(
    "<node-id>", trigger_threshold=90, stop_apps=True, prune_volumes=True
)

[!NOTE] get_location is returned as a raw dict rather than a typed object, since node location payloads vary in shape.

[!WARNING] set_location currently returns a 500 Internal Server Error regardless of the payload sent. Use the Panel to update a node's location until this is resolved.

Barbara Core updates

Barbara Core is the single versioned package that bundles a node's OS and Node Manager together (e.g. Barbara Core 1.10.1.471) — Panel's own update modal shows one version number, not two separate firmwares. See Barbara Core updates in Academy.

client.nodes.update_barbara_core("<node-id>", "update")
client.nodes.update_barbara_core(
    "<node-id>", "schedule", schedule_timestamp="2026-01-01T03:00:00Z"
)
client.nodes.cancel_barbara_core_update("<node-id>")

Docker maintenance and volumes

client.nodes.prune_docker("<node-id>", "prunevolumes")
client.nodes.prune_docker_all("<node-id>")
client.nodes.restart_docker_daemon("<node-id>")

client.nodes.create_docker_volume("<node-id>", "shared-cache")

volumes = client.nodes.list_docker_volumes("<node-id>")
client.nodes.delete_docker_volume("<node-id>", volumes[0]["_id"])

See Volumes in Academy.

[!NOTE] create_docker_volume doesn't return an id. Use list_docker_volumes to look one up before calling delete_docker_volume.

Telemetry

See Telemetry in Academy.

latency = client.nodes.get_telemetry_latency("<node-id>")
client.nodes.set_telemetry_latency("<node-id>", 30)

telemetry = client.nodes.get_last_telemetry("<node-id>")
print(telemetry["disk"], telemetry["alive"])

Node workloads

Deploy and manage applications running on a node: Docker apps (bring-your-own image), Marketplace apps, and Model deployments. See Docker apps and Marketplace apps in Academy.

client.nodes.workloads.create_docker_workload(
    "<node-id>",
    app_version_id="<app-version-id>",
    application_id="<application-id>",
)

client.nodes.workloads.create_marketplace_workload(
    "<node-id>",
    app_version_id="<app-version-id>",
    application_id="<application-id>",
    name="my-workload",
    compose_config=[{"name": "modelservice", "ports": {"PORT_NUMBER": "9083"}}],
)

client.nodes.workloads.start("<node-id>", "<workload-id>")
client.nodes.workloads.stop("<node-id>", "<workload-id>")
logs = client.nodes.workloads.get_logs("<node-id>", "<workload-id>")

[!NOTE] Creation and update calls do not return the resulting workload state. Call client.nodes.workloads.get(...) afterwards if you need it.

[!NOTE] compose_config is a Marketplace/Model-only concept — it's the workload's Compose Config (ports/env/volumes), rewritten into its docker-compose.yml at deploy time. Docker workloads control their own compose file directly, so create_docker_workload/update_docker_workload take no compose_config argument.

A workload's own App Config (as opposed to the node-scoped Global Config above):

client.nodes.workloads.set_app_config("<node-id>", "<workload-id>", config={"threshold": 5})
config = client.nodes.workloads.get_app_config("<node-id>", "<workload-id>")

Model workloads

Same body shape as marketplace workloads, deploying a model application version instead. compose_config must exactly match the service template declared by that model version. See Models in Academy.

client.nodes.workloads.create_model_workload(
    "<node-id>",
    app_version_id="<model-app-version-id>",
    application_id="<model-application-id>",
    name="my-model-workload",
    compose_config=[{"name": "modelservice", "ports": {"PORT_NUMBER": "8501"}}],
)

Node network

The Networking card on the Node Details page in Panel: physical interfaces (Ethernet, WiFi, Mobile), VLANs, hostname, and the Advanced section (VPN, Proxy, Standalone Mode, IPTables). See Network Card overview in Academy.

network = client.nodes.network.get("<node-id>")

client.nodes.network.update_ethernet_interface("<node-id>", "eno1", dhcp=True)
client.nodes.network.enable_proxy("<node-id>", url="http://proxy.example.com:8080")
client.nodes.network.enable_vpn("<node-id>")
client.nodes.network.start_vpn("<node-id>")

[!NOTE] hostname and iptables have no dedicated GET endpoint on the wire either — get_hostname/get_iptables read them back from the node's own document instead (same one client.nodes.get() returns), decoding what the wire base64-encodes.

hostname = client.nodes.network.get_hostname("<node-id>")
client.nodes.network.set_hostname("<node-id>", "floor-2-sensor-01")

iptables = client.nodes.network.get_iptables("<node-id>")  # {"iptables_conf": ..., "iptables_id": ...}
client.nodes.network.update_iptables("<node-id>", iptables_conf="-A INPUT -j ACCEPT")

[!NOTE] create_vlan_interface requires parent_iface_name (the physical interface the VLAN sits on) — this field is entirely missing from the Barbara OpenAPI spec, confirmed instead against Panel's own request payload. It's also the URL path used for creation; delete_vlan_interface/vlan_interface_exists instead key off the VLAN's own name, not the parent's — the two are not interchangeable.

client.nodes.network.create_vlan_interface(
    "<node-id>",
    "eno1",  # parent physical interface
    vlan_id=100,
    name="vlan100",  # the new VLAN's own name
    dhcp=True, ip="", dns="", gateway="", metric=10,
    auto_dns=True, ip_aliases=[], dns_aliases=[],
)
client.nodes.network.vlan_interface_exists("<node-id>", "vlan100")
client.nodes.network.delete_vlan_interface("<node-id>", "vlan100")

[!NOTE] There is no GET/list endpoint for NTP servers — create_ntp_server doesn't return the created entry's id either. Read the current list (including each entry's id, needed for update_ntp_server/delete_ntp_server) from client.nodes.get("<node-id>").raw["deviceConfig"]["ntpServers"] instead; each entry's systemServer flag marks Barbara's own default servers.

Every write in this resource does a best-effort, non-blocking check of the caller's token role before sending the request, and emits a UserWarning if it looks insufficient — the API's own response is always the final authority.

Clusters

See Clusters in Academy.

clusters = client.clusters.list()
cluster = client.clusters.get("<cluster-id>")
cluster = client.clusters.resolve("floor-2-cluster")  # look up by cluster name
client.clusters.update("<cluster-id>", "new-name")

client.clusters.create_global_secrets("<cluster-id>", {"db-password": "s3cr3t"})
client.clusters.set_global_config("<cluster-id>", config={"threshold": 5})

Creating a cluster and managing membership

client.clusters.create(
    "floor-2-cluster",
    primary_node={
        "nodeId": "<node-id>",
        "labels": "eyJ6b25lIjogImZsb29yLTIifQ==",  # base64 JSON: {"zone": "floor-2"}
        "restrictSwarmTrafficToInterface": False,
        "advertiseAddr": "10.0.0.5",
    },
    enable_cluster_volumes=True,
)

client.clusters.join_node(
    "<cluster-id>",
    "<node-id>",
    labels={"zone": "floor-2"},
    restrict_swarm_traffic_to_interface=False,
    advertise_addr="10.0.0.6",
)

client.clusters.pause_node("<cluster-id>", "<node-id>")
client.clusters.drain_node("<cluster-id>", "<node-id>")
client.clusters.set_node_active("<cluster-id>", "<node-id>")
client.clusters.leave_node("<cluster-id>", "<node-id>")

[!NOTE] primary_node and secondary_nodes take the cluster networking configuration as dicts matching the API schema.

Cluster-wide Docker volumes

Unlike node-level Docker volumes, these give a cluster's workloads Swarm-managed high availability — a workload that needs a volume to survive a node failing over must use one of these.

client.clusters.create_swarm_volume("<cluster-id>", "shared-cache")
client.clusters.delete_swarm_volume("<cluster-id>", "<volume-id>")

Docker-native objects declared in an app's own docker-compose.yml (Swarm Config/Swarm Secrets, distinct from the Barbara-managed Global Config/Global Secrets above) are cleaned up the same way: delete_all_swarm_configs, delete_swarm_config, delete_all_swarm_secrets, delete_swarm_secret.

Cluster workloads

The cluster-level equivalent of node workloads: deploy an application across every node in a cluster. Barbara's product docs only ever call this "Workload" — there's no separate "stack" concept in Panel, at either scope. See Add applications in Academy.

client.clusters.workloads.create_docker_workload(
    "<cluster-id>",
    app_version_id="<app-version-id>",
    application_id="<application-id>",
)

client.clusters.workloads.delete("<cluster-id>", "<workload-id>")

Model workloads work the same way, using create_model_workload:

client.clusters.workloads.create_model_workload(
    "<cluster-id>",
    app_version_id="<model-app-version-id>",
    application_id="<model-application-id>",
    name="my-model-workload",
    compose_config=[{"name": "modelservice", "ports": {"PORT_NUMBER": "8501"}}],
)

Applications

See App Library in Academy.

apps = client.applications.list()

client.applications.create(
    "edge-app", "Long description", "Barbara", docker=True, icon_path="./icon.png"
)

client.applications.create_version(
    "<application-id>", "./app-v1.tar", "1.0.0", ["amd64"], ["Initial release"]
)

Deploying a Marketplace/Model app from its published template

A Marketplace or Model workload's compose_config/app_secrets must echo back every service declared in the app version's template, or the API rejects the request — default_services builds that starting point from the template's own defaults, ready to hand to create_marketplace_workload/create_model_workload as-is or after overriding individual ports/volumes/env:

app_version = client.applications.get_version("<application-id>", "<app-version-id>")
compose_config, app_secrets = client.applications.default_services(app_version)

client.nodes.workloads.create_marketplace_workload(
    "<node-id>",
    app_version_id="<app-version-id>",
    application_id="<application-id>",
    name="my-workload",
    compose_config=compose_config,
    app_secrets=app_secrets,
)

[!TIP] create and create_version upload files (icon, installable artifact) as multipart/form-data. Pass a local file path, and the SDK reads the file and builds the request for you.

Models

See Models in Academy.

models = client.models.list()

client.models.create(
    "anomaly-detector", "Long description", "Barbara", model_type=0, engine=0
)

client.models.create_version("<model-id>", "./model.onnx", "1.0.0", ["Initial release"])

[!TIP] sha256 and size for a model version are computed automatically from the artifact, so you don't need to pass them yourself.

Config Repository

Reusable, named configuration documents — typed application or global — that a workload's own App Config or a node's/cluster's own Global Config can reference by id (config_id), rather than being one itself. See Application configuration types in Academy.

config = client.configs.create(
    name="sensor-thresholds",
    description="Per-node alert thresholds",
    config={"temperature_max": 80},
)
print(config.config_type)  # "application" or "global"

client.nodes.set_global_config("<node-id>", config_id=config.id)

Groups

See Nodes list in Academy for group management in the Panel.

group = client.groups.create(
    name="floor-2-sensors",
    description="All floor 2 nodes",
    node_ids=["<node-id-1>", "<node-id-2>"],
)

Users

See Organization in Academy.

users = client.users.list()
page = client.users.paginate(offset=0, size=50)

print(page.items[0].role)  # UserRole.ADMINISTRATOR / SUPERVISOR / EDITOR / VIEWER, or None

Alerts

See the Alert Manager app in Academy.

alerts = client.alerts.list()
client.alerts.ack("<alert-id>")
events = client.alerts.list_events(node_id="<node-id>")

Error handling

All API errors raise a subclass of BarbaraApiError:

from barbara import BarbaraApiError, BarbaraAuthError, BarbaraNotFoundError, BarbaraPermissionError

try:
    client.nodes.resolve("unknown-node")
except BarbaraNotFoundError:
    ...
except BarbaraPermissionError:
    ...
except BarbaraApiError as e:
    print(e.status, e.body)

Architecture

  • One client, one resource tree. BarbaraClient and AsyncBarbaraClient expose the same resources (.nodes, .clusters, .applications, ...) with identical method signatures.
  • Automatic token refresh. A request that receives a 401 is retried once with a freshly fetched token.
  • Typed models. Response entities are plain dataclasses. Every entity keeps the original API payload in .raw.
  • Typed exceptions. BarbaraNotFoundError, BarbaraAuthError, and BarbaraPermissionError subclass BarbaraApiError so callers can handle specific failure modes.
  • Product naming throughout. Classes, methods, and fields follow Barbara's own product terminology (Node, Workload, App Config vs. Global Config, Barbara Core, ...) rather than internal API/wire jargon — see the Barbara Academy docs linked throughout this README for the concepts behind each resource.
  • An escape hatch for everything else. Every resource method calls client.request(method, path, ...) internally, and the same authenticated, token-refreshing request method is available directly for any endpoint not yet wrapped by a typed resource. See the examples. When building path yourself, percent-encode any value that isn't a fixed literal (urllib.parse.quote(value, safe="")). Every typed resource method does this for its own id parameters, but client.request(...) takes path as-is.

API reference

Resource Description
client.nodes Node lifecycle, global secrets, global config, docker credentials, Barbara Core updates, and actions (reboot, provision, ...)
client.nodes.workloads Docker/Marketplace/Model applications deployed on a node
client.nodes.network The Networking card — interfaces, hostname, VLANs, VPN, Proxy, Standalone Mode, IPTables, NTP servers
client.clusters Cluster lifecycle, global secrets, global config, docker credentials, and Swarm-managed volumes
client.clusters.workloads Docker/Marketplace/Model applications deployed across a cluster
client.applications Application catalog and versions
client.models Model catalog and versions
client.configs Config Repository — reusable, named configuration documents
client.groups Node groups
client.users Company users (read-only)
client.alerts Alerts and alert events

Full generated API reference (every method, parameter, and return type): barbaraedge.github.io/barbara-api-sdk-python. For the underlying HTTP API itself, see the Barbara API documentation.

Examples

The examples/ directory has complete, runnable scripts for common use cases:

Script Description
hello_world.py The first script to run: confirm your credentials work and list your nodes
node_info.py Read a node's configuration and latest telemetry
check_barbara_core_updates.py Check nodes for an outdated Barbara Core, optionally update them
network_audit.py Audit a node's network config (interfaces, hostname, VPN, proxy, iptables, NTP), optionally add an NTP server
clone_node.py Clone a node's workloads, config, and docker volumes onto another node
clone_tool.py Back up a node's configuration to a file, restore it onto one or more nodes, or wipe a node
deployment_dashboard.py Live terminal dashboard monitoring a fleet against a target deployment state

Some scripts also demonstrate calling an endpoint through client.request(...) directly (the same low-level method every typed resource is built on) for functionality this SDK doesn't wrap yet.

See examples/README.md for what each one covers, how to configure and run it, and ideas for extending it.

Roadmap

The following areas of the Barbara API are not yet covered by this SDK:

  • App Secrets (Marketplace-only, per-app secrets, distinct from a node's Global Secrets)

Contributing

Bug reports and pull requests are welcome on GitHub Issues. See RELEASING.md for the branch model and release process this repository follows.

License

Distributed under the MIT License. See LICENSE for details.

Release files for barbara-api-sdk 0.6.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for barbara-api-sdk 0.6.0
File Size Uploaded
barbara_api_sdk-0.6.0.tar.gz 139.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for barbara-api-sdk 0.6.0
File Interpreter ABI Platform
barbara_api_sdk-0.6.0-py3-none-any.whl Python 3 none any Details

Total release size: 200.8 kB

Release files / barbara_api_sdk-0.6.0.tar.gz

Download URL barbara_api_sdk-0.6.0.tar.gz
Size 139.5 kB
Tags Source
SHA-256 checksum
How to use checksums
700b2656911ff184f1c3edd5cbe2604cd16c02d4e612c9021be5f96bf79723fd
BLAKE2b-256 checksum
How to use checksums
454c3fe084a08ddb6deb963f02cd8d810db72ddb27ba77bfe11c497f43199f29
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 3, 2026.

Transparency log

Release files / barbara_api_sdk-0.6.0-py3-none-any.whl

Download URL barbara_api_sdk-0.6.0-py3-none-any.whl
Size 61.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
cda5ced7e14e3df0cb82dbd190759573b236adbf9bc0eef12860b860905c65e7
BLAKE2b-256 checksum
How to use checksums
c524016ce3508f5fdd4ecdfe94a4dd345d358d0a760f4b2e6d56340bb20bf529
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 3, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.6.0 This release

2 release files

0.5.0

2 release 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