Script the API
Everything the Console UI does is an HTTP call, and the automation subset of it
is published as /api/v1: a stable, additive-only surface for your own
scripts and pipelines. (Since v0.4.1.)
Basics
Section titled “Basics”- Base URL: your Console, for example
https://console.dednets.com. - Auth:
Authorization: Bearer dak_…with a scoped token (API tokens and scopes). - Format: JSON in. Reads answer JSON; some writes answer a short plain-text
confirmation (
exported,assigned,revoked,ok) rather than a JSON body, so do not pipe every response into a JSON parser. Every error is the JSON envelope described below.
curl -fsS -H "Authorization: Bearer dak_your_token_here" \ https://console.dednets.com/api/v1/topologyReference
Section titled “Reference”Paths below are relative to /api/v1.
| Method + path | Purpose | Scope |
|---|---|---|
GET /ca |
the Console’s CA certificate (public, no auth) | none |
GET /info |
bootstrap document: version, control port, domain, CA fingerprint | read, hosts:manage, services:manage, watchdogs:manage |
GET /limits |
your quota limits and current usage | read, hosts:manage, services:manage, watchdogs:manage |
GET /topology |
the whole account view: hosts, apps, proxy ports, gateways, routes, exposures | read, hosts:manage, services:manage |
GET /services/uptime |
bucketed availability per service | read, services:manage, watchdogs:manage |
A token carrying only the Notifications (notify) scope reaches none of the
authenticated routes above: it is for the notifications API, and /api/v1
answers it with scope_denied.
| Method + path | Purpose | Scope |
|---|---|---|
POST /tokens |
mint a single-use join token for a new or existing host | hosts:manage |
DELETE /daemons?daemon= |
revoke a host and everything it published | hosts:manage |
POST /daemons/update |
pin a host’s version, opt it out, or apply an update | hosts:manage |
Services
Section titled “Services”| Method + path | Purpose | Scope |
|---|---|---|
GET /exports |
published apps | read, services:manage |
POST / DELETE /exports |
publish or unpublish an app | services:manage |
POST / DELETE /proxy-ports |
a local port on one host reaching an app on another | services:manage |
GET /gateways, POST / DELETE /gateways |
publish on a host you own | read for the list, services:manage to change |
GET /exposures, POST / DELETE /exposures |
raw TCP/UDP ports on the DedNets edge | read for the list, services:manage to change |
GET /health-checks, POST / DELETE /health-checks |
per-app health-check overrides | read for the list, services:manage to change |
Watchdogs
Section titled “Watchdogs”| Method + path | Purpose | Scope |
|---|---|---|
GET /watchdogs |
list monitors with their derived status | read, watchdogs:manage |
POST /watchdogs |
create a monitor | watchdogs:manage |
PATCH / DELETE /watchdogs/{id} |
edit or delete | watchdogs:manage |
POST /watchdogs/{id}/pause, .../resume |
pause or resume | watchdogs:manage |
GET /watchdogs/{id}/history |
uptime buckets and the outage list | read, watchdogs:manage |
Account
Section titled “Account”| Method + path | Purpose | Scope |
|---|---|---|
GET / POST / DELETE /apitokens |
manage API tokens | signed-in session only, not available to API tokens |
Recipes
Section titled “Recipes”Enroll a host
Section titled “Enroll a host”curl -fsS -X POST -H "Authorization: Bearer $TOKEN" \ -H 'Content-Type: application/json' \ -d '{"daemon":"vm1","ttl_seconds":3600}' \ https://console.dednets.com/api/v1/tokens# -> {"token":"...","daemon":"alice-vm1"}daemon in the reply is the name the token actually binds: for a new host the
Console derives <username>-<host>. The token is single-use. Then run the
install command on the machine itself: see
Deploy a daemon, or let an agent hand you
the exact command (Let an AI agent run DedNets).
Publish a service as a public HTTPS URL
Section titled “Publish a service as a public HTTPS URL”To publish a container the host has discovered, name it in container:
curl -fsS -X POST -H "Authorization: Bearer $TOKEN" \ -H 'Content-Type: application/json' \ -d '{"daemon":"alice/vm1","container":"my-app","name":"demo","port":8080,"proto":"http","public":true}' \ https://console.dednets.com/api/v1/exportsOmit container and you get a host-port export instead: the daemon dials
its own host (127.0.0.1 unless you set host), which is what you want for a
service running directly on the machine rather than in a container. name is
then required, since there is no container name to fall back on.
curl -fsS -X POST -H "Authorization: Bearer $TOKEN" \ -H 'Content-Type: application/json' \ -d '{"daemon":"alice/vm1","name":"demo","port":8080,"proto":"http","public":true}' \ https://console.dednets.com/api/v1/exportsEither way, read the assigned hostname from the topology afterwards: the app’s
public_host field under its host’s apps list.
curl -fsS -H "Authorization: Bearer $TOKEN" \ https://console.dednets.com/api/v1/topologyTo unpublish: DELETE /api/v1/exports?daemon=alice/vm1&name=demo.
Create a watchdog and ping it
Section titled “Create a watchdog and ping it”curl -fsS -X POST -H "Authorization: Bearer $TOKEN" \ -H 'Content-Type: application/json' \ -d '{"name":"nightly-backup","period":86400,"grace":3600,"group":"backups"}' \ https://console.dednets.com/api/v1/watchdogsperiod and grace are seconds. The reply carries ping_url; have the job
curl it after every successful run. That ping URL is a capability: it
lives at /api/v0/wd/<address> and stays there unversioned, because it is
baked into cron jobs. See
Get alerted when a cron job stops running.
group is optional and purely for display: it buckets related monitors
together in the Console and in listings. Same charset as a name (letters,
digits, - and _, 1 to 64 characters). There is nothing to create first - a
group exists as soon as a watchdog names it, and disappears when the last one
leaves. Omit it for an ungrouped watchdog; responses leave the field out
entirely when a watchdog has no group. (Since v0.4.1.)
Move a watchdog between groups
Section titled “Move a watchdog between groups”PATCH only touches the fields you send, so an absent group leaves the
watchdog where it is. An empty string is the way to ungroup one:
# file it under a groupcurl -fsS -X PATCH -H "Authorization: Bearer $TOKEN" \ -H 'Content-Type: application/json' \ -d '{"group":"db-prod"}' \ https://console.dednets.com/api/v1/watchdogs/12
# remove it from its groupcurl -fsS -X PATCH -H "Authorization: Bearer $TOKEN" \ -H 'Content-Type: application/json' \ -d '{"group":""}' \ https://console.dednets.com/api/v1/watchdogs/12Read uptime
Section titled “Read uptime”curl -fsS -H "Authorization: Bearer $TOKEN" \ "https://console.dednets.com/api/v1/services/uptime?window=day"window is hour, day or week; add &target=alice/vm1/demo for one
service.
Errors
Section titled “Errors”Every /api/v1 failure is one JSON envelope:
{"error":{"code":"not_found","message":"...","status":404}}Codes: invalid_request, unauthorized, forbidden, scope_denied,
quota_exceeded, not_found, method_not_allowed, conflict, too_large,
rate_limited, internal, and error (the fallback for any status the Console
does not map). Branch on code, never on the message text.
What “stable” means here
Section titled “What “stable” means here”- Routes and response fields may be added, never removed or renamed.
- Error codes may be added, never renamed.
- Capability URLs (
/api/v0/wd/…,/install.sh,/dist/…) are deliberately unversioned and stay put.
And the honest boundary: /api/v0 is what the web UI uses. It is not a
contract, it can change without notice, and your scripts should use /api/v1.
Good to know
Section titled “Good to know”- Check the content type. A Console older than v0.4.1 does not know
/api/v1and answers a GET with the web UI’s HTML page and a200. If your client suddenly fails to parse JSON, confirm the response really isContent-Type: application/json, then upgrade the Console. - Watchdog ping URLs can come back relative. On a Console with no public
hostname configured,
ping_urlis a path such as/api/v0/wd/…. Join it to your Console base URL before handing it to a job on another machine. - There is no
GET /api/v1/daemonsand noGET /api/v1/proxy-ports. Both lists come fromGET /api/v1/topology. - Install commands assume HTTPS on port 443. A self-hosted Console served on another port emits an install command whose download URL does not resolve.
See also
Section titled “See also”- Let an AI agent run DedNets for the no-code path
- API tokens and scopes