Skip to content

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.

Information Circle

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,
)
Information Circle

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:

OperationHTTP MethodDescription
get(...)GETRetrieve a single resource by ID
list(...)GETList resources with pagination
post(...)POSTCreate a new resource
patch(...)PATCHPartially update a resource
put(...)PUTFully 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:

AttributeTypeDescription
countintTotal number of results across all pages
nextstr | NoneURL for the next page, or None if this is the last page
previousstr | NoneURL for the previous page
resultslist[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:

ModelDescription
AgentA logical entity that owns channels and receives data
ApplicationAn application definition deployed to agents
DeviceA physical device registered on the platform
OrganisationA top-level organisation that owns agents and users
GroupA grouping of agents for organisational purposes
AppDeploymentA specific deployment of an application
AppInstallAn installation of an application on an agent
UserA 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