Agents & Control API
The ControlClient and AsyncControlClient provide access to the Doover Control API for managing platform resources such as agents, applications, devices, and organisations. Unlike the DataClient, which has fixed methods for each resource type, the Control API uses dynamic resource discovery to determine what operations are available.
Agents can also be listed and inspected from the command line with doover agent list. The CLI supports both flat table and tree output formats.
Creating a Control Client
The constructor accepts the same authentication options as DataClient. The default base URL is https://api.doover.com.
from pydoover.api import ControlClient
# Using a saved profile
client = ControlClient(profile="default")
# Using an explicit token
client = ControlClient(token="eyJhbGciOiJSUzI1NiIs...")
# With a custom base URL and organisation
client = ControlClient(
profile="production",
base_url="https://api.custom-doover.com",
organisation_id=999,
)
The ControlClient uses the Doover2 auth flow (refresh tokens). It does not support the DataService client credentials flow (client_id / client_secret), which is specific to the Data API.
Dynamic Resource Discovery
The Control API is built on a dynamic method registry. Instead of having a fixed get_agent() method, you ask the client for the available methods for a given model, and then call them:
client = ControlClient(profile="default")
# Get the methods available for the Agent model
methods = client.get_control_methods("Agent")
# Retrieve a specific agent by ID
agent = methods.get(agent_id=12345)
print(agent.name)
The get_control_methods method accepts either a model name string or a model class:
from pydoover.models.control import Agent
# These are equivalent
methods = client.get_control_methods("Agent")
methods = client.get_control_methods(Agent)
ControlResourceMethods
The object returned by get_control_methods is a ControlResourceMethods instance that exposes up to five operations:
| Operation | HTTP Method | Description |
|---|---|---|
get(...) | GET | Retrieve a single resource by ID |
list(...) | GET | List resources with pagination |
post(...) | POST | Create a new resource |
patch(...) | PATCH | Partially update a resource |
put(...) | PUT | Fully replace a resource |
Not every model supports all five operations. Use available_operations() to check what is available, or supports() to test for a specific one:
methods = client.get_control_methods("Agent")
# Check which operations are available
print(methods.available_operations())
# e.g. ('get', 'list', 'patch')
# Check for a specific operation
if methods.supports("post"):
new_agent = methods.post(data={"name": "New Agent"})
Calling an unsupported operation raises ControlMethodUnavailableError.
Common Operations
Retrieving a Resource
# Get a single agent
methods = client.get_control_methods("Agent")
agent = methods.get(agent_id=12345)
print(f"Agent: {agent.name} (id={agent.id})")
Listing Resources
The list operation returns a ControlPage with pagination support:
methods = client.get_control_methods("Agent")
# List the first page of agents
page = methods.list()
print(f"Total agents: {page.count}")
print(f"Next page URL: {page.next}")
for agent in page.results:
print(f" {agent.name}")
The ControlPage object has these attributes:
| Attribute | Type | Description |
|---|---|---|
count | int | Total number of results across all pages |
next | str | None | URL for the next page, or None if this is the last page |
previous | str | None | URL for the previous page |
results | list[ControlModel] | The resources on this page |
Updating a Resource
Use patch for partial updates that only modify specified fields:
methods = client.get_control_methods("Agent")
# Update only the name field
updated = methods.patch(
agent_id=12345,
data={"name": "Renamed Agent"},
)
print(f"Updated: {updated.name}")
Available Resource Types
The Control API exposes a wide range of resource types, organised into a group hierarchy. Here are some of the most commonly used ones:
| Model | Description |
|---|---|
Agent | A logical entity that owns channels and receives data |
Application | An application definition deployed to agents |
Device | A physical device registered on the platform |
Organisation | A top-level organisation that owns agents and users |
Group | A grouping of agents for organisational purposes |
AppDeployment | A specific deployment of an application |
AppInstall | An installation of an application on an agent |
User | A platform user account |
The full list can be explored by examining the group tree attributes on the client (e.g., client.agents, client.applications, client.organisations).
File Upload Support
Some Control API operations support file uploads via binary_fields. When a method's endpoint is configured for multipart uploads, the client automatically switches from JSON to multipart form encoding and handles file serialization:
from pathlib import Path
# Upload a file as part of a resource update
# The binary_fields configuration determines which fields are treated as files
methods = client.get_control_methods("Application")
result = methods.patch(
application_id=999,
data={"icon": Path("/path/to/icon.png")},
)
File values can be Path objects, raw bytes, or file path strings. The client detects the type and handles encoding automatically.
Async Control Client
The AsyncControlClient has the same API but all operations return coroutines:
from pydoover.api import AsyncControlClient
async with AsyncControlClient(profile="default") as client:
methods = client.get_control_methods("Agent")
agent = await methods.get(agent_id=12345)
print(agent.name)
Context Manager
Both variants support context managers for automatic cleanup:
# Sync
with ControlClient(profile="default") as client:
methods = client.get_control_methods("Agent")
agent = methods.get(agent_id=12345)
# Async
async with AsyncControlClient(profile="default") as client:
methods = client.get_control_methods("Agent")
agent = await methods.get(agent_id=12345)
Related Pages
- Data Client -- the DataClient for channels, messages, and telemetry
- Authentication -- auth flows and profile management
- Cloud API Overview -- comparison of Data API vs Control API