🛰️ 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.
✅ 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
client.nodes.reboot("<node-id>")
client.nodes.poweroff("<node-id>")
Node secrets
See Secrets in Academy.
client.nodes.create_secrets("<node-id>", {"wifi-psk": "s3cr3t"})
secrets = client.nodes.list_secrets("<node-id>")
client.nodes.delete_secret("<node-id>", "<secret-id>")
Node app configuration
Corresponds to the Panel's Global Config (node-scoped; see Application configuration types for the full picture).
client.nodes.set_appconfig("<node-id>", config={"threshold": 5})
config = client.nodes.get_appconfig("<node-id>")
Docker credentials
client.nodes.create_docker_credentials(
"<node-id>", [{"user": "bob", "password": "s3cr3t", "server": "docker.io"}]
)
Node identity: name, tags, location, safety config
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_config(
"<node-id>", trigger_threshold=90, stop_apps=True, prune_volumes=True
)
Note
get_locationis returned as a raw dict rather than a typed object, since node location payloads vary in shape.
Warning
set_locationcurrently returns a500 Internal Server Errorregardless of the payload sent. Use the Panel to update a node's location until this is resolved.
OTA (Barbara Core version updates)
See Firmware updates in Academy.
client.nodes.send_ota_update("<node-id>", "update")
client.nodes.send_ota_update(
"<node-id>", "schedule", schedule_timestamp="2026-01-01T03:00:00Z"
)
client.nodes.cancel_ota_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_volumedoesn't return an id. Uselist_docker_volumesto look one up before callingdelete_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. See Docker apps and Marketplace apps in Academy.
client.nodes.workloads.create_user_workload(
"<node-id>",
app_version_id="<app-version-id>",
application_id="<application-id>",
)
client.nodes.workloads.create_market_workload(
"<node-id>",
app_version_id="<app-version-id>",
application_id="<application-id>",
name="my-workload",
services=[{"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.
Model workloads
Same body shape as market workloads, deploying a model application version instead — services 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",
services=[{"name": "modelservice", "ports": {"PORT_NUMBER": "8501"}}],
)
Clusters
See Clusters in Academy.
clusters = client.clusters.list()
cluster = client.clusters.get("<cluster-id>")
client.clusters.update("<cluster-id>", "new-name")
client.clusters.create_secrets("<cluster-id>", {"db-password": "s3cr3t"})
client.clusters.set_appconfig("<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_nodeandsecondary_nodestake the cluster networking configuration as dicts matching the API schema.
Cluster stacks
The cluster-level equivalent of node workloads — deploy an application across every node in a cluster. See Add applications in Academy.
client.clusters.stacks.create_user_stack(
"<cluster-id>",
app_version_id="<app-version-id>",
application_id="<application-id>",
)
client.clusters.stacks.delete("<cluster-id>", "<stack-id>")
Model stacks work the same way, using create_model_stack:
client.clusters.stacks.create_model_stack(
"<cluster-id>",
app_version_id="<model-app-version-id>",
application_id="<model-application-id>",
name="my-model-stack",
services=[{"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"]
)
Tip
createandcreate_versionupload files (icon, installable artifact) asmultipart/form-data. Pass a local file path — 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
sha256andsizefor a model version are computed automatically from the artifact — you don't need to pass them yourself.
App configurations
Reusable, named application configurations that can be referenced by ID elsewhere in the API. See Application configuration types in Academy.
app_config = client.appconfig.create(
name="sensor-thresholds",
description="Per-node alert thresholds",
config={"temperature_max": 80},
)
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)
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.
BarbaraClientandAsyncBarbaraClientexpose the same resources (.nodes,.clusters,.applications, ...) with identical method signatures. - Automatic token refresh. A request that receives a
401is 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, andBarbaraPermissionErrorsubclassBarbaraApiErrorso callers can handle specific failure modes. - An escape hatch for everything else. Every resource method calls
client.request(method, path, ...)internally — the same authenticated, token-refreshing request method is available directly for any endpoint not yet wrapped by a typed resource. See the examples. When buildingpathyourself, 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, butclient.request(...)takespathas-is.
📖 API reference
| Resource | Description |
|---|---|
client.nodes |
Node lifecycle, secrets, app configuration, docker credentials, and actions (reboot, provision, ...) |
client.nodes.workloads |
Applications deployed on a node |
client.clusters |
Cluster lifecycle, secrets, app configuration, and docker credentials |
client.clusters.stacks |
Applications deployed across a cluster |
client.applications |
Application catalog and versions |
client.models |
Model catalog and versions |
client.appconfig |
Reusable application configurations |
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 |
|---|---|
quickstart.py |
List nodes and look one up by name |
node_info.py |
Read a node's configuration and latest telemetry |
check_firmware_updates.py |
Check nodes for outdated firmware, optionally update them |
clone_node.py |
Clone a node's workloads and app configuration onto another node |
Each script also demonstrates 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:
- Node network configuration (interfaces, NTP, proxy, VPN, iptables)
- Node standalone mode and VPN peer management
📄 License
Distributed under the 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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file barbara_api_sdk-0.2.1.tar.gz.
File metadata
- Download URL: barbara_api_sdk-0.2.1.tar.gz
- Upload date:
- Size: 81.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/7.0.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fe16d9aff66e24920658687ba9ffded1b89dec9fd15a9096c4326f614a7d3e28
|
|
| MD5 |
f8ce03cc96ac17285fd2857e2720f0d8
|
|
| BLAKE2b-256 |
84104c969078d46bbddd2d6ae6d0100d752d3bc92b5ee738e6a4a58ba76de60a
|
File details
Details for the file barbara_api_sdk-0.2.1-py3-none-any.whl.
File metadata
- Download URL: barbara_api_sdk-0.2.1-py3-none-any.whl
- Upload date:
- Size: 38.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/7.0.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2a1fb5e54b76d35fd488c02a7813d6b27279fef5d2e3d7998a8c089622f22272
|
|
| MD5 |
38f842ab01b681aa5a8a2de00d0e12e3
|
|
| BLAKE2b-256 |
f11cf44af73aaeea2b667d92994291b5ee8700b75223b729064a0e8eb25b44c2
|