Cyberdesk Python SDK
The official Python SDK for Cyberdesk API. This SDK provides a clean, type-safe interface for interacting with all Cyberdesk resources including machines, workflows, runs, connections, and trajectories.
Installation
# Basic SDK installation
pip install cyberdesk
# With testing utilities
pip install "cyberdesk[testing]"
# All development dependencies (for SDK contributors)
pip install "cyberdesk[all]"
Quick Start
The most common use case is to execute workflows that you've created in the Cyberdesk Dashboard.
from cyberdesk import CyberdeskClient, RunCreate
import time
# Initialize the client
client = CyberdeskClient(api_key="your-api-key")
# Create a run for your workflow
run_data = RunCreate(
workflow_id="your-workflow-id",
machine_id="your-machine-id",
input_values={
# Your workflow-specific input data
"patient_id": "12345",
"patient_first_name": "John",
"patient_last_name": "Doe"
}
)
response = client.runs.create_sync(run_data)
if response.error:
print(f"Error creating run: {response.error}")
else:
run = response.data
# Wait for the run to complete
while run.status in ["scheduling", "running"]:
time.sleep(5) # Wait 5 seconds
response = client.runs.get_sync(run.id)
run = response.data
# Get the output data
if run.status == "success":
print("Result:", run.output_data)
else:
print("Run failed:", ", ".join(run.error or []))
Desktop Parameters
Configure machine-specific values that automatically populate workflows running on specific desktops:
from cyberdesk import MachineUpdate
# Set desktop parameters for a machine
client.machines.update_sync(
machine_id="machine-id",
data=MachineUpdate(
machine_parameters={
"username": "machine_specific_user",
"api_url": "https://api-region-east.example.com",
"config_path": "C:\\MachineA\\config.json"
},
machine_sensitive_parameters={
"password": "actual_secret_value", # Stored securely in Basis Theory
"api_key": "actual_api_key_123"
}
)
)
Use in workflows with standard syntax:
Log in to {api_url} using {username} and password {$password}.
Then open the config at {config_path}.
Desktop parameters automatically override run-level inputs and persist across runs. See the Desktop Parameters docs for more details.
Full Documentation
For complete documentation including async/sync usage, error handling, and all available methods, visit:
https://docs.cyberdesk.io/sdk-guides/python
Async Support
The SDK provides both synchronous and asynchronous methods for all operations:
import asyncio
from cyberdesk import CyberdeskClient
async def main():
client = CyberdeskClient(api_key="your-api-key")
# Async method
response = await client.machines.list()
if response.data:
for machine in response.data.items:
print(f"Machine: {machine.name}")
asyncio.run(main())
Resources
Machines
# List machines
response = client.machines.list_sync(
skip=0,
limit=10,
status=MachineStatus.ACTIVE
)
# Get a specific machine
response = client.machines.get_sync("machine-id")
# Create a machine
from cyberdesk import MachineCreate
machine = MachineCreate(
name="My Machine",
provider="azure",
region="eastus"
)
response = client.machines.create_sync(machine)
# Update a machine
from cyberdesk import MachineUpdate
update = MachineUpdate(name="Updated Name")
response = client.machines.update_sync("machine-id", update)
# Delete a machine
response = client.machines.delete_sync("machine-id")
Workflows
# List workflows
response = client.workflows.list_sync()
# Get a workflow
response = client.workflows.get_sync("workflow-id")
# Create a workflow
from cyberdesk import WorkflowCreate
workflow = WorkflowCreate(
name="My Workflow",
description="Description"
)
response = client.workflows.create_sync(workflow)
# Update a workflow
from cyberdesk import WorkflowUpdate
update = WorkflowUpdate(description="New description")
response = client.workflows.update_sync("workflow-id", update)
# Delete a workflow
response = client.workflows.delete_sync("workflow-id")
Runs
# List runs with filtering
response = client.runs.list_sync(
workflow_id="workflow-id",
machine_id="machine-id",
status=RunStatus.RUNNING
)
# Create a run
from cyberdesk import RunCreate
run = RunCreate(
workflow_id="workflow-id",
machine_id="machine-id"
)
response = client.runs.create_sync(run)
# Get run details
response = client.runs.get_sync("run-id")
# Update run status
from cyberdesk import RunUpdate
update = RunUpdate(status=RunStatus.COMPLETED)
response = client.runs.update_sync("run-id", update)
Connections
# List connections
response = client.connections.list_sync(
machine_id="machine-id",
status=ConnectionStatus.ACTIVE
)
# Create a connection
from cyberdesk import ConnectionCreate
connection = ConnectionCreate(
machine_id="machine-id",
connection_type="vnc"
)
response = client.connections.create_sync(connection)
Trajectories
# List trajectories
response = client.trajectories.list_sync(workflow_id="workflow-id")
# Create a trajectory
from cyberdesk import TrajectoryCreate
trajectory = TrajectoryCreate(
workflow_id="workflow-id",
trajectory_data=[{"action": "click", "x": 100, "y": 200}]
)
response = client.trajectories.create_sync(trajectory)
# Get latest trajectory for a workflow
response = client.trajectories.get_latest_for_workflow_sync("workflow-id")
# Update a trajectory
from cyberdesk import TrajectoryUpdate
update = TrajectoryUpdate(
trajectory_data=[{"action": "type", "text": "Hello"}]
)
response = client.trajectories.update_sync("trajectory-id", update)
Error Handling
All methods return an ApiResponse object with data and error attributes:
response = client.machines.get_sync("invalid-id")
if response.error:
print(f"Error: {response.error}")
else:
print(f"Machine: {response.data.name}")
Context Manager
The client can be used as a context manager:
with CyberdeskClient(api_key="your-api-key") as client:
response = client.machines.list_sync()
# Client will be properly closed when exiting the context
Configuration
You can specify a custom API base URL:
client = CyberdeskClient(
api_key="your-api-key",
base_url="https://custom-api.cyberdesk.io"
)
Type Safety
The SDK is fully typed and exports all request/response models:
from cyberdesk import (
MachineCreate,
MachineUpdate,
MachineResponse,
MachineStatus,
WorkflowCreate,
WorkflowResponse,
RunCreate,
RunStatus,
# ... and more
)
Webhooks
Handling Webhooks
Cyberdesk sends webhooks when important events occur (e.g., workflow completion). To handle webhooks:
import json
import os
from fastapi import FastAPI, Request, HTTPException
from svix.webhooks import Webhook, WebhookVerificationError
from openapi_client.cyberdesk_cloud_client.models.run_completed_event import RunCompletedEvent
from openapi_client.cyberdesk_cloud_client.models.run_response import RunResponse
from typing import cast
app = FastAPI()
@app.post("/webhooks/cyberdesk")
async def cyberdesk_webhook(request: Request):
secret = os.environ["SVIX_WEBHOOK_SECRET"]
wh = Webhook(secret)
payload = await request.body()
headers = {
"svix-id": request.headers.get("svix-id"),
"svix-timestamp": request.headers.get("svix-timestamp"),
"svix-signature": request.headers.get("svix-signature"),
}
try:
wh.verify(payload, headers)
data = json.loads(payload)
# IMPORTANT: Use from_dict() for attrs classes, NOT model_validate()
evt = RunCompletedEvent.from_dict(data)
run: RunResponse = cast(RunResponse, evt.run)
# Process the run based on status
if run.status == "success":
print(f"Run {run.id} completed successfully!")
print(f"Output: {run.output_data}")
return {"ok": True}
except WebhookVerificationError:
raise HTTPException(status_code=400, detail="Invalid signature")
Important: The SDK uses attrs classes (not Pydantic), so use RunCompletedEvent.from_dict(data) instead of model_validate(data).
Testing Webhooks Locally
See tests/webhooks/WEBHOOK_TESTING.md for a complete guide on testing webhooks locally without exposing your server to the internet.
Quick start:
# Install SDK with testing dependencies
pip install "cyberdesk[testing]"
# Set up .env file
cp .env.example .env
# Edit .env with your SVIX_WEBHOOK_SECRET
# Run the example webhook handler
python -m uvicorn tests.webhooks.example_webhook_handler:app --reload
# In another terminal, test it
python -m tests.webhooks.test_webhook_local
Example Files
All webhook testing resources are in the tests/webhooks/ directory:
tests/webhooks/example_webhook_handler.py- Complete, production-ready webhook handlertests/webhooks/test_webhook_local.py- Utility to test webhooks locallytests/webhooks/test_webhook_integration.py- pytest integration teststests/webhooks/WEBHOOK_TESTING.md- Comprehensive testing guidetests/webhooks/WEBHOOK_QUICKSTART.md- Quick reference guide
Testing
The SDK includes comprehensive testing utilities:
Quick Test
# Install with test dependencies
pip install ".[testing]"
# Set up environment
cp .env.example .env
# Edit .env with your credentials
# Run all tests
pytest tests/ -v
Test Categories
- Webhook Tests (
tests/webhooks/) - Test webhook handlers locally, no real API - Integration Tests (
tests/integration/) - Test SDK with real API calls
See TESTING.md for complete testing documentation.
Limitations
- Screenshot API: The
/v1/computer/{machine_id}/display/screenshotendpoint is not included in the generated client due to limitations with binary (PNG) responses in the openapi-python-client generator. This can be added manually if needed - see the TODO comment inclient.py.
Requirements
- Python 3.8+
- httpx
- attrs (used by the auto-generated OpenAPI client)
License
MIT License - see LICENSE file for details.
Metadata
Release files for cyberdesk 2.2.69
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| cyberdesk-2.2.69.tar.gz | 170.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| cyberdesk-2.2.69-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 670.3 kB
Release files / cyberdesk-2.2.69.tar.gz
| Download URL | cyberdesk-2.2.69.tar.gz |
|---|---|
| Size | 170.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
d910ce3d7233bed2ef04b143377634720cdaef0761e5695883ecaea9b3325e08
|
|
BLAKE2b-256 checksum How to use checksums |
ea8876d89338aefabedd6ea0b69fb44c694af2806cbb3276948d9dafd8516e1f
|
| 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 Oct 1, 2026.
Transparency logRelease files / cyberdesk-2.2.69-py3-none-any.whl
| Download URL | cyberdesk-2.2.69-py3-none-any.whl |
|---|---|
| Size | 500.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
6b5d8b856023656894676e7de360e218a303fd79b972278086c5e509d48bfdd8
|
|
BLAKE2b-256 checksum How to use checksums |
e83399938f35c75c4b2c0d7e3d2d5c94806fa0503b770b9b7b9e8bf2df311158
|
| 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 Oct 1, 2026.
Transparency log