Skip to main content
The devhelm Python package is a typed, synchronous client for the DevHelm REST API. It’s built on httpx, ships full Pydantic models for every request and response, and has full mypy strict-mode type coverage.
The Python SDK is synchronous. There’s no AsyncDevhelm client as of v1.3.0 — for concurrent usage, run the sync client in a thread pool (see async usage).

Install

pip install devhelm
Requires Python 3.11 or later.

Initialize

import os
from devhelm import Devhelm

client = Devhelm(token=os.environ["DEVHELM_API_TOKEN"])
Constructor parameterTypeDefaultDescription
tokenstr""API token. Falls back to the DEVHELM_API_TOKEN env var when empty.
base_urlstrhttps://api.devhelm.ioAPI base URL — override for self-hosted.
org_idstr | NoneNoneSets x-phelm-org-id header. Falls back to DEVHELM_ORG_ID env var, then "1".
workspace_idstr | NoneNoneSets x-phelm-workspace-id header. Falls back to DEVHELM_WORKSPACE_ID env var, then "1".
timeoutfloat30.0Per-request timeout in seconds.

Create a monitor

The SDK accepts request bodies as plain dicts (validated against the Pydantic request model) or as Pydantic instances. Both produce the same MonitorDto response:
monitor = client.monitors.create({
    "name": "API Health",
    "type": "HTTP",
    "config": {"url": "https://api.example.com/health", "method": "GET"},
    "frequencySeconds": 60,
    "regions": ["us-east", "eu-west"],
})

print(f"Created monitor: {monitor.id}")
managedBy is optional — omit it (defaults to API server-side) or set it explicitly to one of API, DASHBOARD, CLI, TERRAFORM, or MCP to record the provenance for drift detection. Or using the typed request class:
from devhelm import CreateMonitorRequest

monitor = client.monitors.create(
    CreateMonitorRequest(
        name="API Health",
        type="HTTP",
        config={"url": "https://api.example.com/health", "method": "GET"},
        frequencySeconds=60,
    )
)

List monitors

monitors = client.monitors.list()  # auto-paginates through every page

for monitor in monitors:
    print(f"{monitor.name}: {monitor.current_status}")
For manual pagination control:
page = client.monitors.list_page(page=0, size=50)
print(f"Total: {page.total_elements}")
for monitor in page.data:
    ...

Get check results

results = client.monitors.results(monitor.id, limit=10)

for check in results.data:
    status = "OK" if check.passed else "FAIL"
    print(f"{check.region}: {status} ({check.response_time_ms}ms)")
results() returns a CursorPage — call it with cursor=results.next_cursor to fetch the next batch.

Manage incidents

incidents = client.incidents.list()  # auto-paginates

for incident in incidents:
    if incident.status == "OPEN":
        print(f"Open: {incident.title}")

client.incidents.resolve(incident.id)
# or with a resolution note:
client.incidents.resolve(incident.id, {"body": "fixed by deploy"})

Create an alert channel

channel = client.alert_channels.create({
    "name": "Slack Alerts",
    "config": {
        "channelType": "slack",
        "webhookUrl": os.environ["SLACK_WEBHOOK_URL"],
    },
})
The channel type is the channelType discriminator inside config (slack, email, discord, pagerduty, …) — there is no top-level type field on this request.

Error handling

from devhelm import (
    Devhelm,
    DevhelmApiError,
    DevhelmAuthError,
    DevhelmNotFoundError,
    DevhelmRateLimitError,
)

try:
    client.monitors.get("nonexistent-id")
except DevhelmNotFoundError:
    print("Monitor not found")
except DevhelmRateLimitError as e:
    print(f"Rate limited (request_id={e.request_id}) — back off and retry")
except DevhelmAuthError:
    print("Bad token or insufficient permissions")
except DevhelmApiError as e:
    print(f"{e.status}: {e.message}")
Full taxonomy in the Error handling guide.

Next steps

Client reference

Every resource and method available on the client.

Concurrency patterns

Run the sync client concurrently with thread pools and asyncio.