DashCaddy Documentation
API and Automation
DashCaddy is more than a dashboard — it exposes a real API and automation surface so you can drive deployments, DNS, proxy, certificates, monitoring, and operations programmatically or through AI.
Every action available in the DashCaddy UI is also available through a programmatic surface: a versioned REST API, a JavaScript automation layer, an AI Intent Router for natural-language commands, an MCP Server for AI assistant integration, a WebSocket channel for real-time events, a Prometheus endpoint for metrics, and a plugin system for extending the platform. This guide covers each surface with concrete examples.
Whether you are wiring DashCaddy into a CI/CD pipeline, building a custom dashboard, or letting an AI assistant manage your infrastructure, the automation layer is designed to be the primary interface — the web UI is just one consumer of it.
REST API
All platform operations are available under /api/v1/. The API covers service management, app deployment, DNS automation, Caddy reverse-proxy integration, certificate workflows, health and status reporting, user and admin operations, backup/restore, and more. The repository ships with an OpenAPI definition so the public contract can mature into a full reference.
Requests and responses are JSON. The base URL is your DashCaddy host — for example https://dashcaddy-host/api/v1/services. All endpoints require authentication (see below) and return structured error codes rather than opaque messages.
# List all services
curl -H "Authorization: Bearer ***" \
https://dashcaddy-host/api/v1/services
# Deploy from a template
curl -X POST -H "Authorization: Bearer ***" \
-H "Content-Type: application/json" \
-d '{"template":"jellyfin","name":"media","hostname":"media.lab"}' \
https://dashcaddy-host/api/v1/services
# Restart a service
curl -X POST -H "Authorization: Bearer ***" \
https://dashcaddy-host/api/v1/services/media/restartAuthentication
DashCaddy supports two authentication methods, chosen by how you access the API:
Session cookie (browser)
The web dashboard authenticates with a session cookie set after login (email magic link or username/password with optional TOTP 2FA). API calls made from the browser carry the cookie automatically. This is the right method for in-dashboard automation and userscripts.
API key (Bearer token)
For server-to-server automation, scripts, and integrations, use an API key. Generate keys from Settings → API Keys. Keys are bearer tokens — pass them in the Authorizationheader on every request:
Authorization: Bearer dc_live_xxxxxxxxxxxxxxxxxxxxSecurity: API keys grant the same permissions as the user who created them, scoped by RBAC role. Store keys in a secret manager — never commit them to source control. Rotate keys immediately if one is leaked.
Rate limiting
The API applies per-token rate limiting to protect the platform from runaway scripts and abusive clients. Limits are generous for normal operation: interactive dashboard usage will never hit them. If a client exceeds the limit, the API responds with 429 Too Many Requests and a Retry-After header indicating when to retry. Back off and retry — do not hammer the endpoint.
For high-volume automation (e.g. polling service status in a tight loop), prefer the WebSocket channel or the Prometheus endpoint over repeated REST polling. Both are designed for frequent reads and do not count against the REST rate limit.
JavaScript automation
For programmatic automation, use the REST API directly with fetch or any HTTP client. The API is JSON-based, uses Bearer token authentication, and returns structured error codes. Here is a minimal helper you can drop into any Node.js, Bun, or browser project:
class DashCaddy {
constructor(opts) {
this.baseUrl = opts.baseUrl;
this.token = opts.token;
}
async request(path, options) {
options = options || {};
var url = this.baseUrl + "/api/v1" + path;
var res = await fetch(url, {
method: options.method || "GET",
body: options.body,
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer " + this.token
}
});
var body = await res.json();
if (!res.ok) throw { code: body.error, status: res.status };
return body;
}
// List services
services() { return this.request("/services"); }
// Deploy from template
deploy(template, name, hostname) {
return this.request("/services", {
method: "POST",
body: JSON.stringify({ template, name, hostname })
});
}
// Restart a service
restart(id) {
return this.request("/services/" + id + "/restart", { method: "POST" });
}
}Every request returns a structured JSON response or throws an error object carrying the error code, HTTP status, and message — so your automation can branch on specific failure conditions.
AI Intent Router
The AI Intent Routeraccepts natural-language commands and translates them into real infrastructure actions through the same API. This turns ad-hoc operator requests (“restart the media server”, “is postgres up?”, “deploy redis”) into reproducible, logged operations — no need to remember endpoint paths or parameter names.
The router parses intent, maps it to the correct API call, executes it, and returns both a human-readable summary and the raw API result. Every intent execution is recorded in the audit log just like a manual action.
# Natural-language operation
POST /api/v1/ai/intent
{
"message": "Restart the media server and check its health"
}
# Response
{
"summary": "Restarted 'media' and confirmed health: healthy",
"actions": [
{ "method": "POST", "path": "/api/v1/services/media/restart", "status": 200 },
{ "method": "GET", "path": "/api/v1/services/media/health", "status": 200 }
]
}Example intents: “deploy the postgres template as db on db.lab”, “list all unhealthy services”, “rotate the TLS cert for wiki.lab”, “create a DNS record for api.labpointing at 10.0.0.5”.
MCP Server
The built-in MCP (Model Context Protocol) Server exposes DashCaddy operations as tools that AI assistants and external automation can call directly. Connect your assistant to the MCP endpoint and it can list services, deploy templates, manage DNS, inspect health, and trigger operations — all through the standard MCP tool interface, with full audit logging.
To connect Claude Desktop, GPT, or another MCP-compatible assistant, add the DashCaddy MCP server to your client's MCP configuration:
{
"mcpServers": {
"dashcaddy": {
"url": "https://dashcaddy-host/mcp",
"headers": {
"Authorization": "Bearer dc_live_xxxxxxxxxxxxxxxxxxxx"
}
}
}
}Once connected, the assistant discovers DashCaddy's tools automatically and can invoke them in response to your requests — “ask DashCaddy which services are down”, “have DashCaddy deploy Grafana”, etc. This is the most natural way to operate infrastructure through conversation.
WebSocket real-time events
The dashboard subscribes to a WebSocket channel for live updates: service health changes, container starts and stops, deployment progress, DNS changes, and fleet events arrive in real time without polling. You can consume the same channel in your own dashboards, chatops bots, or automation.
const ws = new WebSocket('wss://dashcaddy-host/api/v1/events', {
headers: { Authorization: 'Bearer ' + process.env.DC_TOKEN },
});
ws.on('message', (data) => {
const event = JSON.parse(data);
console.log(event.type, event.payload);
});Common event types you will see on the channel:
| Event type | Emitted when |
|---|---|
service.health | A service transitions between healthy / unhealthy / down |
service.started | A container starts successfully |
service.stopped | A container stops (graceful or crash) |
deploy.progress | A template deployment advances through its stages |
deploy.complete | A deployment finishes (success or failure) |
dns.changed | A DNS record is created, updated, or removed |
proxy.updated | A Caddy route is applied or removed |
cert.issued | A TLS certificate is issued or renewed |
fleet.host | A fleet host changes state (Premium) |
audit.event | A user or API action is logged for audit |
Prometheus metrics endpoint
DashCaddy exposes a Prometheus-format metrics endpoint at /metrics for service health, container status, request counts, certificate expiry, and system indicators. Scrape it with Prometheus and visualize in Grafana. See Integrations for a full scrape config.
# Scrape config (prometheus.yml)
scrape_configs:
- job_name: 'dashcaddy'
metrics_path: /metrics
static_configs:
- targets: ['dashcaddy-host:3000']
# Sample exported metrics
dashcaddy_service_health{service="media"} 1
dashcaddy_container_running{container="db"} 1
dashcaddy_http_requests_total{service="wiki",code="200"} 48213
dashcaddy_cert_expiry_days{domain="media.lab"} 87Health and readiness probes
Two lightweight probes let orchestrators and load balancers check DashCaddy itself:
# Liveness — is the process up?
GET /healthz
# Readiness — can it serve (Docker, Caddy, DNS connected)?
GET /readyzUse /healthz for container restart policies and /readyz for traffic gating. If /readyz fails but /healthz passes, a dependency (Docker socket, Caddy Admin API, or Technitium DNS) is unreachable — see Troubleshooting.
Plugin & extension system
DashCaddy includes a plugin/extension system with hooks into the deployment, DNS, proxy, and monitoring pipelines. Write extensions to react to service lifecycle events, inject custom Caddy directives, emit additional metrics, or integrate third-party tools — without forking the core.
Plugins register for lifecycle hooks (e.g. onServiceDeployed, onDnsRecordCreated,onProxyRouteApplied) and receive a context object they can act on. A plugin can modify the generated Caddyfile before it is applied, push a notification when a service goes unhealthy, or export custom metrics alongside the built-in ones. Plugins are loaded at startup and run in the same process.
Structured error codes
The API returns 80 structured error codes across 12 modules rather than opaque messages, so your automation can branch on specific failure conditions — DNS token invalid, Caddy unreachable, license expired, rate limited — instead of parsing strings. Every error response includes the machine-readable code, the HTTP status, and a human-readable message.
| Module | Example error codes |
|---|---|
| auth | AUTH_INVALID_TOKEN, AUTH_PERMISSION_DENIED, AUTH_2FA_REQUIRED |
| service | SERVICE_NOT_FOUND, SERVICE_ALREADY_EXISTS, SERVICE_UNHEALTHY |
| deploy | DEPLOY_TEMPLATE_INVALID, DEPLOY_PORT_CONFLICT, DEPLOY_FAILED |
| dns | DNS_TOKEN_INVALID, DNS_ZONE_NOT_FOUND, DNS_RECORD_EXISTS |
| proxy | PROXY_CADDY_UNREACHABLE, PROXY_CONFIG_INVALID, PROXY_UPSTREAM_TIMEOUT |
| cert | CERT_ISSUANCE_FAILED, CERT_EXPIRED, CERT_NOT_TRUSTED |
| license | LICENSE_EXPIRED, LICENSE_INVALID, LICENSE_MACHINE_LIMIT |
| user | USER_NOT_FOUND, USER_ALREADY_EXISTS, USER_INVITE_EXPIRED |
| backup | BACKUP_FAILED, BACKUP_CORRUPT, RESTORE_CONFLICT |
| recipe | RECIPE_INVALID, RECIPE_COMPONENT_FAILED (Premium) |
| swarm | SWARM_NOT_INITIALIZED, SWARM_NODE_UNREACHABLE (Premium) |
| fleet | FLEET_HOST_OFFLINE, FLEET_DEPLOY_PLAN_FAILED (Premium) |
Handle errors by code in your automation:
try {
await dc.services.deploy({ template: 'postgres', name: 'db', hostname: 'db.lab' });
} catch (err) {
if (err.code === 'DEPLOY_PORT_CONFLICT') {
// pick a different port and retry
} else if (err.code === 'LICENSE_EXPIRED') {
// alert ops to renew
} else {
throw err; // unknown — surface to the operator
}
}Why automation matters
DashCaddy can execute the full infrastructure chain around a service, not just report its state after the fact. Between the REST API, the AI Intent Router, MCP, WebSockets, Prometheus, and the plugin system, you have every surface you need to make DashCaddy a first-class citizen of your automation stack. Start with a simple curl call, and add AI and event-driven flows as your needs grow.
For the infrastructure that backs all of this, see Integrations. When things go wrong, the Troubleshooting guide walks each layer with commands and fixes.