Preserve selected memory freshness and the VM/LXC interpretation. Retain both native tool contracts and current API concurrency controls. Qualify this source separately from release and installed recovery. Contract-Neutral: Integrate already-governed parents without a new contract change. Incoming verification files already match main. Fresh integration tests and source-bound browser proof cover the combined memory and installer surfaces.
97 KiB
🔌 Pulse API Reference
Pulse provides a comprehensive REST API for automation and integration.
Base URL: http://<your-pulse-ip>:7655/api
🔐 Authentication
Most API requests require authentication via one of the following methods:
API Token (Recommended)
Create an API token in Pulse's API Access settings with only the scopes
needed for your task. The read-only example below requires monitoring:read.
Pass the token in the X-API-Token header, using a private header file so the
secret does not appear in shell history or process arguments.
On a trusted machine, as the account running curl, prepare the file and open it in an editor. This preserves an existing file, restricts its directory and stops before editing if preparation fails or a credential path is symlinked or not a regular file:
(
set -eu
umask 077
auth_dir="$HOME/.config/pulse"
auth_file="$auth_dir/api-header"
if [ -L "$HOME/.config" ] || [ -L "$auth_dir" ] || [ -L "$auth_file" ]; then
printf 'Refusing a symlinked credential path.\n' >&2
exit 1
fi
if [ -e "$auth_file" ] && [ ! -f "$auth_file" ]; then
printf 'Credential file must be a regular file.\n' >&2
exit 1
fi
mkdir -p "$auth_dir"
chmod 700 "$auth_dir"
touch "$auth_file"
chmod 600 "$auth_file"
vi "$auth_file"
)
In the editor, save just this line, replacing <token> with the API token:
X-API-Token: <token>
Define this helper in the same Bash session (curl 7.76 or later). The examples on this page use it to make one request, print only the HTTP status and save each response to a new owner-only file. This keeps resource names, audit details and credential-bearing error bodies out of terminal output and recordings.
pulse_api() (
set -eu
umask 077
if [ "$#" -ne 2 ]; then
printf 'Usage: pulse_api GET|POST|PUT|DELETE /api/path\n' >&2
exit 2
fi
method=$1
api_path=$2
case "$api_path" in
/api/*) ;;
*) printf 'Use an API path on this Pulse instance, not a full URL.\n' >&2; exit 2 ;;
esac
case "$method" in
GET) set -- ;;
POST) set -- --header 'Content-Type: application/json' --request POST --data-binary @- ;;
PUT) set -- --header 'Content-Type: application/json' --request PUT --data-binary @- ;;
DELETE) set -- --request DELETE ;;
*) printf 'This example helper accepts only GET, POST, PUT and DELETE.\n' >&2; exit 2 ;;
esac
auth_dir="$HOME/.config/pulse"
auth_file="$auth_dir/api-header"
if [ -L "$HOME/.config" ] || [ -L "$auth_dir" ] || [ -L "$auth_file" ] || [ ! -f "$auth_file" ]; then
printf 'Prepare a regular, private header file first.\n' >&2
exit 1
fi
chmod 700 "$auth_dir" || exit "$?"
chmod 600 "$auth_file" || exit "$?"
result_file=$(mktemp "$auth_dir/api-response.XXXXXX") || exit "$?"
curl_exit=0
status=$(curl --disable --fail-with-body --silent --show-error --globoff \
--proto '=http' --noproxy 127.0.0.1 --connect-timeout 5 --max-time 20 \
--header "@$auth_file" --output "$result_file" --write-out '%{http_code}' \
"$@" "http://127.0.0.1:7655$api_path") || curl_exit=$?
printf 'HTTP %s\nPrivate response: %s\n' "$status" "$result_file"
[ "$curl_exit" -eq 0 ] || exit "$curl_exit"
case "$status" in
2??) ;;
*) exit 1 ;;
esac
)
Then make a read-only request with a monitoring:read token:
pulse_api GET /api/state/summary
The default loopback origin applies only when curl runs on the Pulse host and
explicitly bypasses proxy environment settings. For remote access, replace
http://127.0.0.1:7655 in the helper with your Pulse HTTPS origin, and replace
--proto '=http' --noproxy 127.0.0.1 with --proto '=https'. Keep certificate
verification enabled; for a private CA, add --cacert with a CA file you
verified separately, never --insecure. Do not put credentials in the URL.
Open the reported response file privately to inspect it, including on failure; share only the relevant redacted error, not the whole response or file. Each call preserves earlier responses. HTTP 401 and 403 return curl's non-zero exit and retain the error body; redirects are not followed and are not success. The connection limit is five seconds and the whole request limit is twenty seconds. A partial file after a transport error is not a complete result. No request is retried automatically, especially a POST whose outcome is uncertain: inspect the existing action and target before deciding what to do. A 2xx response is only HTTP success, not proof that an action completed.
The helper also supports the existing RBAC administration examples. POST and PUT read their JSON body from standard input; GET and DELETE send no body. A method being supported is not permission to change a resource. Choose each administrative change separately and check its current state afterwards. A timeout or incomplete response can occur after a change was applied; do not repeat a POST, PUT or DELETE merely because the response was lost.
Keep --globoff: shell quotes alone do not disable curl's URL globbing. Without
it, a path containing a brace list such as {review,ops} or a range such as
[1-3] can send several requests, including several POST, PUT or DELETE
requests. The helper treats these characters literally, not as a batch of
targets. Still quote the whole path in the shell, and URL-encode dynamic resource
IDs and query values; --globoff does not encode them for you.
Keep --disable first: it ignores local curl configuration that could enable
trace output, bypass TLS or follow redirects. On an authentication-enabled
instance, the protected summary checks token access; /api/health is public
and does not verify authentication. The helper is local example code, not an
installed Pulse command; define it again in a new shell before using these
examples. Dispose of response files under your normal private-data policy.
Do not paste tokens into command lines, URLs or issue reports. Keep the header file private and outside shared repositories and diagnostics; do not use curl verbose/trace output when sharing a result. Share only the relevant redacted error, not the whole infrastructure response. Revoke tokens that are no longer needed in Pulse's API Access settings.
Bearer Token
The same API token can use a Bearer header instead. Replace the header file's line in the editor with the following, then use the same helper above; do not send both authentication headers:
Authorization: Bearer <token>
Session Cookie
For one-off read-only diagnostics, use your signed-in Pulse browser to open the API path on that same instance. Do not extract or paste its session cookie into a command or report; use the scoped token-file method above for automation. State-changing session requests also require Pulse's CSRF protection, which the UI handles.
Session endpoints:
POST /api/login(sets the session cookie,pulse_sessionover plain HTTP or__Host-pulse_sessionover HTTPS, pluspulse_csrf)POST /api/logout(clears session)
Login body:
{ "username": "admin", "password": "secret", "rememberMe": true }
Public endpoints include:
GET /api/healthGET /api/versionGET /api/agent/version(agent update checks)GET /api/setup-script(public; the generated script prompts for its setup token at runtime and skips auto-registration if none is supplied)
🔏 Scopes and Admin Access
Some endpoints require admin privileges and/or scopes. Common scopes include:
monitoring:readmonitoring:writesettings:readsettings:writeagent:config:readagent:manageactions:plan,actions:approve,actions:executeaudit:read
Endpoints that require admin access are noted below.
Admin endpoints are instance administration unless they act only on the
request organization. On instance administration an API token must not be
bound to any organization other than the default one: a token bound to a
tenant organization gets 403 whatever its scopes, as that organization's
owner does. Endpoints that serve only the request organization, such as nodes
and platform connections, agents, install commands, configuration transfer and
reports, accept tokens bound to that organization with the listed scope.
📡 Core Endpoints
System Health
GET /api/health
Check if Pulse is running.
{
"status": "healthy",
"timestamp": 1700000000,
"uptime": 3600,
"devModeSSH": false
}
System State
GET /api/state
Returns the complete state of your infrastructure (Nodes, VMs, Containers, Storage, Alerts). This is the main endpoint used by the dashboard.
GET /api/state/summary
Returns a lightweight integration summary for external dashboards and checks. Requires monitoring:read.
{
"activeAlerts": 1,
"nodes": 2,
"vms": 8,
"containers": 12,
"dockerHosts": [
{
"name": "Docker Host",
"containers": 5,
"uptimeSeconds": 86400,
"cpuUsagePercent": 12.5
}
],
"verdicts": {
"ok": 17,
"attention": 1,
"critical": 1,
"stale": 1,
"off": 2,
"unknown": 0
},
"attention": [
{
"id": "node-1",
"name": "PVE Node 1",
"type": "agent",
"platformType": "proxmox",
"verdict": "critical",
"topReason": { "code": "offline" }
}
],
"lastUpdate": "2026-05-24T10:11:12Z"
}
verdicts uses the canonical ok, attention, critical, stale, off,
and unknown fleet-health vocabulary. attention is severity ordered and
capped at 30 entries. Powered-off workloads are neutral (off), and stale or
missing telemetry is never reported as ok.
Simple Stats (HTML)
GET /simple-stats
Lightweight HTML status page for quick checks.
Unified Resources
GET /api/resources
Returns the unified resource list with pagination + aggregations. Requires monitoring:read.
Query params:
type: comma-separated list (e.g.,agent,vm,system-container,app-container,docker-service,storage,pbs,pmg,k8s-cluster,k8s-node,pod,k8s-deployment,physical_disk,ceph)source: comma-separated list (e.g.,proxmox,agent,docker,pbs,pmg,kubernetes)excludeSource: comma-separated list of sources that must not be present on a matching resourcestatus: comma-separated list (online,offline,warning,unknown)parent: parent resource IDcluster: cluster namenamespace: Kubernetes namespace (filters Kubernetes resources only)q: name search (contains match)tags: comma-separated tagspage: page number (default1)limit: page size (default50, max100)sort:name(default),status,type,lastSeenorder:asc(default) ordesc
Note: GET /api/resources is optimized for list views. Some large, platform-specific fields may be omitted from the list response and are only returned by GET /api/resources/{id}.
Each list resource can include a backend-owned health envelope with a
canonical verdict and stable reasons (code plus optional compact
detail). Consumers should use this envelope for cross-platform posture
instead of deriving health from provider-specific status strings.
Guest readings: availability and age
In legacy VM payloads, disk.usage: -1 means unavailable, not negative usage.
In GET /api/resources and GET /api/resources/{id}, an unavailable disk
reading is omitted as metrics.disk. Do not replace an absent metric with
zero; a genuinely observed zero is valid. Allocated virtual-disk capacity
alone does not establish used space inside the guest.
Read proxmox.diskStatusReason alongside the value. A prev- reason means
retained disk evidence, not a fresh reading. agent-not-running can also
represent a general guest-agent HTTP 500 error: it does not establish that the
guest's service is absent or stopped. An absent reason alone does not prove
freshness either.
For memory, use metrics.memory.observation for the selected metric's
source and age, not a different source's retained proxmox.memory facet:
Observation state |
Meaning for the consumer |
|---|---|
current |
Accepted source observation; a normal cache hit can keep the original observedAt. Check its age for your use. |
last-known |
Retained evidence, not a new measurement. Keep its original age visible; do not record it as a fresh sample. |
unavailable |
No usable usage observation; do not treat retained numbers or zero-valued fields as a measurement. |
| Missing or unrecognised | Freshness is unknown, including older payloads without an observation. Do not infer current. |
observedAt belongs to that source observation. If it is absent or in the
future, its age is unknown; do not substitute the resource's lastSeen or
updatedAt. In the raw proxmox.memory facet, usageUnavailable: true can
preserve known capacity without measured usage. An absent metrics.memory
is not 0% usage. Conversely, an independently observed PVE or Pulse-agent
memory reading can remain current while QEMU disk collection is deferred;
disk state is not memory provenance.
A running VM, an advancing row timestamp or an OK backup does not prove fresh guest readings or successful thaw. Do not install or restart an agent or send live guest-agent probes merely to fill these gaps, especially during a backup, freeze/thaw or an unresponsive-guest incident. Follow Backup safety and missing-reading guidance. Recovery needs independent thaw, fresh successful writes to every covered filesystem and workload liveness; retained API values cannot establish it.
Availability is an additive resource facet. availability is the compatibility
summary used by existing clients; availabilityChecks contains every check
attached to the resource. Each check can include correlationState
(attached, standalone, ambiguous, or unresolved), its correlation
rule/reason/candidate count, and an evidence envelope with observation and
validity timestamps. Attached targets also add a checks relationship and do
not appear as separate network-endpoint rows.
GET /api/resources/stats
Returns aggregations (counts + health rollups).
GET /api/resources/k8s/namespaces?cluster=<clusterName>
Returns namespace-level rollups (pods + deployments) for a Kubernetes cluster. Requires monitoring:read.
{
"cluster": "prod-k8s",
"data": [
{
"namespace": "default",
"pods": { "total": 12, "online": 10, "warning": 2, "offline": 0, "unknown": 0 },
"deployments": { "total": 3, "online": 3, "warning": 0, "offline": 0, "unknown": 0 }
}
]
}
GET /api/resources/{id}
Fetch a single resource by ID.
GET /api/resources/{id}/children
Returns child resources for the parent ID.
GET /api/resources/{id}/metrics
Returns the resource metrics payload.
POST /api/resources/{id}/link (monitoring:write)
Manually link two resources.
{ "targetId": "resource-id", "reason": "optional note" }
POST /api/resources/{id}/unlink (monitoring:write)
Manually unlink two resources.
{ "targetId": "resource-id", "reason": "optional note" }
POST /api/resources/{id}/report-merge (monitoring:write)
Report an incorrect merge (creates exclusions).
{ "sources": ["proxmox", "agent"], "notes": "optional note" }
Resource Maintenance and Operator State
Use a timed maintenance window to suppress alerts during planned work on a
resource. This API, including descendant scope, is available in stable v6.4.1.
Choose the resource's id from GET /api/resources and URL-encode it in the
request path. Do not substitute a display name or a Proxmox VM number.
| Endpoint | Required scope | Result |
|---|---|---|
GET /api/resources/{id}/operator-state |
monitoring:read |
Returns the saved state, or HTTP 404 with operator_state_not_set when no state is saved. |
PUT /api/resources/{id}/operator-state |
monitoring:write |
Replaces the entire state and returns the saved record. |
DELETE /api/resources/{id}/operator-state |
monitoring:write |
Removes the entire state, returning HTTP 204 even if it was already absent. |
PUT replaces the whole record, not just the fields supplied. Read the current
state first and preserve unrelated settings, such as monitoring mode, lifecycle,
remediation policy, criticality and notes. Treat only operator_state_not_set
as an empty record, and stop on other read errors. Coordinate concurrent writers
so a read followed by a PUT does not overwrite someone else's changes.
For a one-time window, merge these fields into that record, replacing the example times with your intended start and end:
{
"maintenanceStartAt": "2026-10-15T20:00:00Z",
"maintenanceEndAt": "2026-10-15T20:30:00Z",
"maintenanceScope": "resource_and_descendants",
"maintenanceReason": "Proxmox node maintenance"
}
This is a set of fields to merge, not a complete replacement for an existing
record. Remove any maintenanceRecurrence when replacing a recurring schedule
with this one-time window. The API rejects a record containing both.
| Field | Meaning |
|---|---|
maintenanceStartAt, maintenanceEndAt |
RFC3339 timestamps. Supply both, with the end strictly after the start. The window is active from the start, inclusive, until the end, exclusive. |
maintenanceScope |
resource applies only to this resource and is the default. resource_and_descendants also covers descendants in Pulse's resource hierarchy, such as a node's guests. |
maintenanceReason |
Optional explanation of the planned work. |
The response includes maintenanceWindowActive and, while active,
maintenanceActiveStartAt and maintenanceActiveEndAt. The server sets the
record's setAt and setBy fields from the request, ignoring client values.
During an active window, matching new alerts and firing/recovery notifications are suppressed. Applying an active window also clears matching existing alerts from the active list, retaining history. This is broader than pausing delivery for an existing incident. Resources outside the scope remain monitored normally.
The window expires automatically, with no re-enable request needed. Subsequent observations can raise alerts again, subject to normal alert policy and any other active maintenance window. Previously cleared alerts are not automatically restored, and expiry does not replay a backlog of maintenance notifications.
To end maintenance early, read the latest record, remove its maintenance fields and PUT the remaining state back. Do not DELETE the record unless you also intend to remove its other operator settings. An inherited window must be changed on the ancestor that owns it.
Fleet Connections
GET /api/connections
Returns the canonical fleet connections ledger with per-row fleet-governance state. Requires admin access with settings:read.
The payload is the source of truth for enrollment, liveness, version drift, adapter health, config rollout, credential posture, update posture, and remote-control posture. Consumers must not rebuild those states from provider-specific config stores or display labels.
Define the private-response helper in Authentication,
with settings:read on the token:
pulse_api GET /api/connections
Unified Action Planning
The existing action API separates planning, approval and execution. Run these
steps separately, inspecting each response before moving on; do not paste the
whole sequence as an unattended recovery script. Use the private header file
and pulse_api helper from Authentication, not a token in a
command or environment assignment. Inspect each reported private response file
before proceeding. For remote access, change the helper as described there,
using your Pulse HTTPS origin with certificate verification enabled.
| Endpoint | Token scope and access |
|---|---|
GET /api/agent/resource-capabilities/{id} |
monitoring:read; lists the resource's advertised capabilities and parameter schemas. |
POST /api/actions/plan |
actions:plan and permission to plan actions; creates a plan but does not approve or execute it. |
POST /api/actions/{id}/decision |
actions:approve and permission to approve actions; records an explicit approved or rejected decision for a pending action, without executing it. |
POST /api/actions/{id}/execute |
actions:execute and permission to execute actions; dispatches only an approved action or an approval-free executable plan. Dry-run-only plans cannot execute. |
GET /api/audit/actions |
audit:read, audit-log read permission and licensed audit logging. |
GET /api/audit/actions/{id}/events |
The same audit access; returns the action's lifecycle evidence. |
The legacy ai:execute scope also permits planning, approval and execution,
but use the narrower action scopes when possible. Approval and execution tokens
must be bound to an authorised user; a scope alone does not bypass approval
policy, separation of duties or step-up requirements. Audit reads through a
browser session additionally require admin access. A monitoring:read token is
not an action-control or audit token.
- Inspect capabilities. Choose the canonical resource
idfromGET /api/resources, not a display name or a VM number. Replacevm:42throughout these examples and URL-encode it in request paths (vm%3A42here). An empty capabilities list means there is nothing to plan for that resource.
pulse_api GET /api/agent/resource-capabilities/vm%3A42
- Plan only. Use an advertised capability and its actual parameter schema;
restartandmode=gracefulare examples, not universal capabilities. Choose a request ID for this specific intent. Pulse derives the actor from the authenticated credential, not a caller-suppliedrequestedByvalue.
pulse_api POST /api/actions/plan <<'JSON'
{
"requestId": "manual-recovery-123",
"resourceId": "vm:42",
"capabilityName": "restart",
"params": { "mode": "graceful" },
"reason": "Recover after confirmed outage"
}
JSON
Inspect the returned approval policy, blast radius, expiry and preflight checks.
Keep its actionId and reviewed planHash; replace act_... and sha256:...
below with those returned values. Planning is not proof that execution is safe
or currently available.
- Decide only if approval is required. Use an authorised approver's private header file. Approve only the reviewed plan; stop on a refusal or stale-plan response rather than weakening the policy or creating another recovery action.
pulse_api POST /api/actions/act_.../decision <<'JSON'
{
"outcome": "approved",
"reason": "Inside maintenance window",
"planHash": "sha256:..."
}
JSON
- Execute separately. Only after reviewing a successful decision, or an approval-free executable plan, use the executor's authorised header file:
pulse_api POST /api/actions/act_.../execute <<'JSON'
{
"reason": "Execute approved recovery",
"planHash": "sha256:..."
}
JSON
If the response is lost or times out, inspect the existing action and target before retrying: losing the connection does not prove that execution stopped. An HTTP success is not a substitute for checking the terminal result and the resource's observed state.
- Read the audit and events, using a private header file with audit access:
pulse_api GET '/api/audit/actions?resourceId=vm%3A42&limit=10'
pulse_api GET /api/audit/actions/act_.../events
When audit logging is unavailable, use Pulse's action detail in the signed-in UI to inspect the existing action; do not infer success or repeat execution from an unavailable audit response.
Response:
{
"actionId": "act_...",
"requestId": "manual-recovery-123",
"allowed": true,
"requiresApproval": true,
"approvalPolicy": "admin",
"predictedBlastRadius": ["vm:42", "node-1"],
"rollbackAvailable": false,
"message": "Plan created for restart on web-42. Execution requires admin approval and is not performed by this endpoint.",
"plannedAt": "2026-05-03T10:00:00Z",
"expiresAt": "2026-05-03T10:05:00Z",
"resourceVersion": "resource:sha256:...",
"policyVersion": "policy:sha256:...",
"planHash": "sha256:...",
"preflight": {
"target": "vm:42",
"currentState": "web-42 is warning",
"intendedChange": "Restart the VM",
"dryRunAvailable": false,
"dryRunSummary": "No provider-supported dry run is advertised for this capability.",
"safetyChecks": [
"Resource was resolved from the unified resource registry.",
"Capability is advertised by the resource contract.",
"This endpoint plans only; it does not approve or execute the action.",
"Execution requires admin approval."
],
"verificationSteps": [
"Refresh the resource and confirm the expected state after execution.",
"Review /api/audit/actions/{actionId}/events for lifecycle evidence."
],
"generatedAt": "2026-05-03T10:00:00Z"
}
}
Resource Metadata
User notes, tags, and custom URLs for resources.
These routes do not require admin access. Any authenticated browser session can call them, and API tokens need the scope shown.
-
GET /api/agents/metadata(monitoring:read) -
GET /api/agents/metadata/{agentId}(monitoring:read) -
PUT /api/agents/metadata/{agentId}(monitoring:write) -
DELETE /api/agents/metadata/{agentId}(monitoring:write) -
GET /api/guests/metadata(monitoring:read) -
GET /api/guests/metadata/{guestId}(monitoring:read) -
PUT /api/guests/metadata/{guestId}(monitoring:write) -
DELETE /api/guests/metadata/{guestId}(monitoring:write) -
GET /api/docker/metadata(monitoring:read) -
GET /api/docker/metadata/{containerId}(monitoring:read) -
PUT /api/docker/metadata/{containerId}(monitoring:write) -
DELETE /api/docker/metadata/{containerId}(monitoring:write) -
GET /api/docker/runtimes/metadata(monitoring:read) -
GET /api/docker/runtimes/metadata/{runtimeId}(monitoring:read) -
PUT /api/docker/runtimes/metadata/{runtimeId}(monitoring:write) -
DELETE /api/docker/runtimes/metadata/{runtimeId}(monitoring:write)
Version Info
GET /api/version
Returns version, build time, and update status.
Example response:
{
"version": "6.0.0",
"buildTime": "2026-02-21T00:00:00Z",
"channel": "stable",
"deploymentType": "systemd",
"updateAvailable": false,
"latestVersion": "6.0.0"
}
Version fields are returned as plain semantic versions (no leading v).
🖥️ Nodes & Config
Public Config
GET /api/config
Returns a small public config payload (update channel, auto-update enabled).
List Nodes
GET /api/config/nodes (admin, settings:read)
This administrative inventory covers saved PVE, PBS and PMG connections,
not individual VMs or agents. Keep its response private. Use the returned
connection id for updates, deletion and tests; a display name, VMID or another
installation's node name is not that ID. Re-read the inventory before a change
rather than guessing an ID from a list position.
Add Node
POST /api/config/nodes (admin, settings:write)
Adding a connection saves credentials and enables collection; it is not a passive validation step. Use an existing dedicated, least-privilege platform token. The Pulse API token in the private header file authorises this configuration request; the Proxmox token in the JSON body authorises reads from the monitored platform. They are different credentials. Do not use a root password or widen a department-scoped token to make denied peer reads succeed; follow the scoped-access checks. For PBS and PMG, use their PBS and PMG token guidance. PBS password-based onboarding can also try to create a token and ACLs on the PBS server; it is not read-only.
Prepare the request in a private editor, not a credential-bearing shell assignment, here-document or command argument. Use the same Bash session as the private-response helper:
node_request_dir=$(umask 077; mktemp -d "${TMPDIR:-/tmp}/pulse-node.XXXXXX") &&
node_request_file="$node_request_dir/node.json" &&
(umask 077; set -C; : > "$node_request_file") &&
vi "$node_request_file"
If preparation or the editor fails, stop. In that editor, enter the following
JSON, replacing the example hostname, connection name, full platform token ID
(user@realm!token) and token-secret placeholder with your intended values:
{
"type": "pve",
"name": "PVE connection A",
"host": "https://pve.example.net:8006",
"tokenName": "pulse-monitor@pve!pulse-readonly",
"tokenValue": "<platform-token-secret>",
"verifySSL": true
}
Set verifySSL explicitly to true: omission on these new-connection and
test request paths does not enable certificate verification. The example
requires a certificate trusted by the Pulse server for that hostname. For a
self-signed/private certificate, also supply fingerprint with the SHA-256
certificate fingerprint verified through an independent trusted channel.
A fingerprint merely fetched from the endpoint is not established trust.
Do not clear an existing pin, set verifySSL to false, or change to HTTP to
work around a certificate failure.
After reviewing this one intended configuration change, send the file:
pulse_api POST /api/config/nodes < "$node_request_file"
The helper retains the response privately and makes one bounded request, without retries. A lost response can follow a successful save: re-read the saved inventory before deciding whether another request is needed. HTTP success does not establish fresh readings for every node, guest, backup or History view. Keep both request and response files out of reports and shared repositories; remove the temporary request under your credential policy when no longer needed.
Test Connection
POST /api/config/nodes/test-connection (admin, settings:write)
Tests supplied credentials with live platform requests without saving the connection. Use the same private JSON-file transport and explicit TLS settings, only when a live connection test is appropriate; it is not a required precursor to filing a report. Success covers the reads performed by that test, not all collection permissions, guest recovery or sustained monitoring.
Test Node Config (No Save, Live Request)
POST /api/config/nodes/test-config (admin, settings:write)
This also contacts the platform using the supplied credentials; no save does not mean offline schema validation. Do not run either test merely to collect new evidence during a freeze/thaw, backup or unresponsive-host incident. Use existing observations and the safe diagnostic guidance.
Update Node
PUT /api/config/nodes/{id} (admin, settings:write)
Review the current connection and submit only the intended supported changes from a private JSON file. Do not copy masked credentials from a GET response back as new secrets. Credential rotation and trust changes are separate decisions; preserve the existing credential and fingerprint when not changing them. A response failure does not prove the update was unapplied.
Delete Node
DELETE /api/config/nodes/{id} (admin, settings:write)
Removes the Pulse connection, not the platform workloads or the upstream token. Collection and related alert coverage can stop. Check the exact connection and arrange any needed independent coverage before an intentional removal; do not delete and re-add a connection to diagnose missing readings or identity mixing.
Test Node (Legacy)
POST /api/config/nodes/{id}/test (admin, settings:write)
Uses the saved connection for a live test. It is not a passive read of its last result or a guarantee that ordinary collection recovered.
An optional JSON body can override verifySSL and fingerprint for this test
without changing the saved connection or credentials. Omitted fields keep their
saved values. Send false to disable CA verification or "" to clear the saved
fingerprint for the test. A nonempty fingerprint remains enforced regardless of
verifySSL. Empty-body requests keep the saved TLS settings. For example,
{"verifySSL":true,"fingerprint":"<SHA-256 fingerprint>"} tests an edited pin.
Refresh Cluster Nodes
POST /api/config/nodes/{id}/refresh-cluster (admin, settings:write)
Actively rediscovers a saved PVE connection's cluster membership. It does not select authorised nodes, grant missing permissions or repair an unrelated standalone installation. Keep intentional token restrictions intact.
Export Configuration
POST /api/config/export (instance admin for the default organization, tenant
manager for a selected tenant, or an API token bound to the selected
organization with settings:read)
Request body:
{ "passphrase": "use-a-strong-passphrase" }
Returns an encrypted export bundle in data. Passphrases must be at least 12 characters.
Import Configuration
POST /api/config/import (instance admin for the default organization, tenant
manager for a selected tenant, or an API token bound to the selected
organization with settings:write)
Request body:
{
"data": "<exported-bundle>",
"passphrase": "use-a-strong-passphrase"
}
🧭 Setup & Discovery
Setup Script (Public)
GET /api/setup-script
Returns the Proxmox/PBS setup script as a shell-script download. Accepts an
optional legacy setup_token query for compatibility. Current downloads
contain no token: paste the separately revealed token only at the silent
terminal prompt, or supply PULSE_SETUP_TOKEN_FILE pointing to a mode-0600
regular file in a mode-0700 directory owned by the script's user. Never put a
token in a copied command or URL. Canonical callers must send a supported type of pve or
pbs plus non-empty host and pulse_url; the route no longer generates
placeholder-host scripts for later repair or reconstructs Pulse identity from
the request origin. The route now shares the same canonical type boundary as
/api/setup-script-url, rejecting unsupported node types instead of treating
unknown values as PBS, and it normalizes the supplied host before script
generation so downloaded artifacts and rerun URLs preserve the same canonical
node identity as the bootstrap response. The optional backup_perms=true
query is supported only for type=pve.
Setup Script URL
POST /api/setup-script-url (admin, settings:write)
Generates a one-time setup token and URL for /api/setup-script.
Canonical callers must send a supported type of pve or pbs plus a
non-empty host; the backend normalizes that host before minting the setup
token and now returns the canonical bootstrap identity back in the response as
type, host, url, downloadURL, scriptFileName, command, commandWithEnv,
commandWithoutEnv, setupToken, tokenHint, and expires. The returned
commands are canonical root-or-sudo curl -fsSL bootstrap commands for the
generated setup script, while url, downloadURL, and scriptFileName are
the runtime-owned artifact metadata used by copy and manual download surfaces.
The request body is a single canonical JSON object only; unknown fields and trailing JSON are
rejected as invalid request shape, and backupPerms:true is supported only for
type:"pve". This route stays on the normal
authenticated bootstrap boundary: when Pulse auth is already configured it
requires a real authenticated session or API token, and setup tokens do not
authorize the request itself. Pulse-managed Proxmox monitor-token names on the
setup/bootstrap path derive from the canonical Pulse endpoint, not request-local
host fallbacks, so setup-script and turnkey node-add flows stay on one
deterministic pulse-<canonical-scope-slug> identity per Pulse instance.
The command, commandWithEnv, and commandWithoutEnv fields now contain
identical credential-free commands. They download the complete script before
running it and prompt silently in the root-or-sudo process. The token crosses
the installer boundary through a private file, not process arguments or an
exported secret. downloadURL equals the tokenless url, so a manual download
also needs the separately revealed token at runtime. setupToken is used for
/api/auto-register; tokenHint remains masked on the setup page. Settings
reveals the token in a separate dialog: run the command first, then copy and
paste the token only at its prompt. The artifact is reused only for the same
host and options while its five-minute expiry is live, and discarded when the
setup modal closes.
Non-frontend consumers must validate the complete artifact, including the
canonical host, type, URLs, filename, masked hint and live expiry. Current
Unified Agents and the shell installer also accept the coherent older-server
artifact during upgrades, but never execute its command text. For new
Proxmox agent enrolment against a newer server, use its current installer;
already enrolled agents keep reporting normally.
Auto-Register (Public)
POST /api/auto-register
Registers a node through the canonical /api/auto-register contract using a temporary
setup token carried in the JSON authToken field. Canonical callers must send
a supported type of pve or pbs, an explicit source marker of agent or
script, a canonical Pulse-managed tokenId in the form
pulse-monitor@{pve|pbs}!pulse-<canonical-scope-slug> matching the requested
type, an explicit serverName, and missing-token requests now fail with
Pulse setup token required. Incomplete token completion requests now fail
with tokenId and tokenValue must be provided together, and other missing
canonical request fields fail with explicit Missing required canonical auto-register fields: ... guidance.
Success responses now carry the canonical stored identity and caller boundary
back to the installer or runtime-side Unified Agent:
{"status":"success","action":"use_token","type":"pve|pbs","source":"agent|script","host":"https://...","nodeId":"<stored-name>","nodeName":"<stored-name>",...}.
Agent Install Command
POST /api/agent-install-command (admin, settings:write)
Generates an API token and install command for agent-based Proxmox setup.
Discovery
GET /api/discover (admin, settings:write)
Returns the last cached network discovery result from the background discovery service. It does not scan.
POST /api/discover (admin, settings:write)
Runs a manual network scan. JSON body: {"subnet": "...", "use_cache": false}. subnet defaults to auto; use_cache: true returns the cached result instead of scanning.
Test Notification
POST /api/test-notification (admin, settings:write)
Broadcasts a WebSocket test event.
📊 Metrics & Charts
Chart Data
GET /api/charts?range=1h
Returns time-series data for CPU, Memory, and Storage.
Ranges: 5m, 15m, 30m, 1h, 4h, 12h, 24h, 7d
Storage Charts
GET /api/storage-charts
Returns storage chart data.
Storage Stats
GET /api/storage/{storageId} (monitoring:read)
Returns one storage resource by its id, wrapped as {"data": {...}, "timestamp": 1700000000}. The data object includes id, name, node, instance, type, status, total, used, free, usage, content, shared, enabled and active. Returns HTTP 404 with code storage_not_found for an unknown ID, and HTTP 405 for methods other than GET. A request without an ID (GET /api/storage/) returns HTTP 400 with code missing_storage_id.
Recovery (formerly Backups / Snapshots)
Pulse v6 uses the recovery API to provide a platform-agnostic view of backup and snapshot artifacts. The endpoints below carry the provider-neutral contract covering subjects, points, rollups, posture, and filter semantics.
GET /api/recovery/points- Query params:
- Core filters:
provider,kind,mode,outcome,subjectResourceId,rollupId - Time window:
from(RFC3339),to(RFC3339) - Paging:
page,limit - Normalized filters:
q,cluster,node,namespace,scope=workload,verification(verified|unverified|unknown)
- Core filters:
- Query params:
GET /api/recovery/rollups- Query params:
provider,kind,mode,outcome,subjectResourceId,rollupId,from(RFC3339),to(RFC3339),page,limit
- Query params:
GET /api/recovery/postures- Returns server-derived per-resource protection posture and provider evidence quality.
- Query params: repeated
resourceIdvalues (maximum 200),state(protected|attention|unprotected|unknown),page,limit(maximum 200) - Batch clients must make one bounded request per 200 resource ids, never one request per table row.
- Unknown identity, permission, history, or collection completeness remains
unknown; clients must not infer a healthier state from raw backup or snapshot artifacts.
GET /api/recovery/series- Returns per-day counts for the activity chart.
- Query params: same filters as
/api/recovery/points(except paging), plustzOffsetMinutes(integer; UTC offset minutes for day bucketing)
GET /api/recovery/facets- Returns distinct filter values (clusters/nodes/namespaces) and capability flags (size/verification/entity id present).
- Query params: same filters as
/api/recovery/points(except paging)
🔔 Notifications
Send Test Notification
POST /api/notifications/test (admin, settings:write)
Sends one test notification through a single channel per request, not all configured channels. The JSON body method (or type) selects email, webhook, or apprise and uses the saved configuration. webhookId targets one specific saved webhook. config tests unsaved email or Apprise settings. If alert delivery is paused, the response includes deliveryPaused: true.
Email, Apprise, and Webhooks
GET /api/notifications/email(admin,settings:read;settings:writealso accepted)PUT /api/notifications/email(admin,settings:write)GET /api/notifications/apprise(admin,settings:read;settings:writealso accepted)PUT /api/notifications/apprise(admin,settings:write)GET /api/notifications/webhooks(admin,settings:read;settings:writealso accepted)POST /api/notifications/webhooks(admin,settings:write)PUT /api/notifications/webhooks/<id>(admin,settings:write)DELETE /api/notifications/webhooks/<id>(admin,settings:write)POST /api/notifications/webhooks/test(admin,settings:write)GET /api/notifications/webhook-templates(admin,settings:read;settings:writealso accepted)GET /api/notifications/webhook-history(admin,settings:read;settings:writealso accepted)GET /api/notifications/email-providers(admin,settings:read;settings:writealso accepted)
Email and webhook configurations accept optional tagFilter (an array of
resource-tag strings) and tagFilterMode ("all" or "any"). An omitted
field is preserved on update; an empty tagFilter clears routing and restores
delivery for all resources.
GET /api/notifications/health(admin,settings:read;settings:writealso accepted)- Queue health is
degradedwhenever any retainedfailedordlqdelivery exists, andunavailablewhen queue state cannot be read. Recoverable failed attempts that returned topendingfor retry do not degrade health. queue.attention_requiredis the retained terminal-failure count.reason_codesidentifies failed and/or dead-letter state without exposing notification content. Counts are retention-bounded: sent, failed, and cancelled rows are retained for 7 days; dead-letter rows for 30 days.
- Queue health is
Audit Webhooks (Pro)
GET /api/admin/webhooks/audit(admin,settings:read)POST /api/admin/webhooks/audit(admin,settings:write)- Body:
{ "urls": ["https://..."] } - Strict JSON contract: unknown fields and trailing payload are rejected.
- Maximum
20webhook URLs per update request. - URLs are normalized (trimmed) and duplicate entries are ignored.
- Endpoint fails closed if URL validation runtime is unavailable.
- Body:
Advanced Reporting (Pro)
Report generation uses settings:read. For single-resource and fleet PDF
narration, an API token also needs ai:execute (or full access). Without it,
the report still succeeds with a deterministic summary and no AI provider call;
a single-resource PDF also explains the missing scope. Browser sessions and
server-run schedules keep their existing narration behaviour. AI budgets still
apply. GET and CSV never use AI narration.
GET /api/admin/reports/catalog(admin,settings:read)- Returns the canonical reporting catalog for the settings surface, including locked-state teaser copy, enabled-surface guidance copy, performance-report options, canonical single-report filename subject, canonical fallback filename date style, and the nested VM inventory export definition.
- Metadata route: readable without the
advanced_reportingfeature so locked admin surfaces can render the same reporting definition before upsell.
POST /api/admin/reports/generate(admin,settings:read)- Body fields:
format(pdf/csv, defaultpdf),resourceType,resourceId,metricType(optional),start/end(RFC3339, optional; defaults to last 24h),title(optional) - If
titleis omitted, the backend applies the canonical default title for that resource report. - When narration is permitted and Pulse Assistant is configured, the configured AI provider narrates a PDF's executive summary; each narration counts against the AI budget and appears in AI usage. If the call fails, times out or the budget is spent, the PDF falls back to the deterministic summary. CSV output is never narrated. The settings UI uses this method.
- API-token callers also need the
ai:executescope for AI narration. With onlysettings:readthe report is still produced, with the deterministic summary and a note saying why.
- Body fields:
GET /api/admin/reports/generate(admin,settings:read)- Takes the same fields as query params and returns the same report, except that it never calls an AI provider: a PDF carries the deterministic summary and a note saying AI narration needs
POST. Other methods return405withAllow: GET, POST.
- Takes the same fields as query params and returns the same report, except that it never calls an AI provider: a PDF carries the deterministic summary and a note saying AI narration needs
POST /api/admin/reports/generate-multi(admin,settings:read)- Body fields:
resources(1-50 entries of{resourceType,resourceId}),format,metricType(optional),start/end(RFC3339, optional; defaults to last 24h),title(optional) - If
titleis omitted, the backend applies the canonical default fleet report title. - AI narration of the fleet summary follows the same rule as single reports: API-token callers need
ai:execute, otherwise the deterministic summary is used. CSV output is never narrated.
- Body fields:
GET /api/admin/reports/inventory/vms/export(admin,settings:read)- Query params:
format(csvonly; optional and defaults tocsv) - Exports the current fleet-wide VM inventory as spreadsheet-friendly CSV using the canonical runtime model.
- Query params:
Validation and limits:
startandendmust be RFC3339 when provided.- Malformed
start/endvalues,endnot strictly afterstart, or report windows over 366 days return400 invalid_time_range. metricTypemust match[a-zA-Z0-9._:-]+and be <= 64 chars, otherwise400 invalid_metric_type.titlemust be <= 256 chars, otherwise400 invalid_title.- Report body max size is 1MB; oversized payloads return
400 body_too_large. - Report bodies reject trailing payload and unknown JSON fields with
400 invalid_body. - VM inventory export only accepts
csv.
Common reporting error codes:
invalid_format,missing_params,invalid_resource_type,invalid_resource_idinvalid_metric_type,invalid_title,invalid_time_rangeno_resources,too_many_resources,body_too_large,invalid_body
Queue and Dead-Letter Tools
GET /api/notifications/delivery-log?limit=200(admin,settings:read)- Returns recent per-attempt delivery evidence with destination identifiers, alert identifiers, outcomes, timestamps, failure classes, and redacted error text. Completed attempts are retained for 7 days; dead-letter attempts are retained for 30 days, so the response reports both windows explicitly.
limitdefaults to 50 and is capped at 200.
GET /api/notifications/queue/stats(admin,settings:readandmonitoring:read)- Returns counts for all rows still retained by the queue. Status counts have different retention windows and are not a delivery rate or lifetime total.
GET /api/notifications/dlq(admin)POST /api/notifications/dlq/retry(admin)POST /api/notifications/dlq/delete(admin,settings:write;DELETEalso accepted)- Body:
{ "id": "<notification id>" }. Marks that queue rowcancelledwith the note "Manually deleted from DLQ"; the row is not removed and is retained for 7 days like other cancelled rows. The endpoint does not check that the row is in the dead-letter queue. - Returns
{ "success": true, "message": "Notification deleted from DLQ", "id": "<notification id>" }. An empty or missingid, or an invalid JSON body, returns400. An unknownidreturns500.
- Body:
POST /api/notifications/terminal-failures/retry(admin,settings:write)- Returns every retained
failedordlqdelivery topendingwith a fresh retry budget. Existing per-attempt delivery history is preserved.
- Returns every retained
POST /api/notifications/terminal-failures/dismiss(admin,settings:write)- Marks every retained terminal delivery
cancelled, clearing the active queue-health warning without deleting delivery history.
- Marks every retained terminal delivery
🚨 Alerts
Alert configuration and history (requires monitoring:read/monitoring:write).
For planned downtime, use Resource Maintenance and Operator State. For an existing incident whose notifications should be paused without clearing it, use the snooze and unsnooze endpoints below.
GET /api/alerts/configPUT /api/alerts/config— replaces only the top-level keys the body carries; a key left out keeps its stored value, and the merged config is normalized as before. The body must be a JSON object.GET /api/alerts/deadman/config— returns only whether an external watchdog is configured;pingUrlis***REDACTED***when present and never returns the credential-bearing URLPUT /api/alerts/deadman/config— body{ "pingUrl": "..." }; accepts a healthchecks-compatible base success URL,***REDACTED***to preserve the saved value, or an empty string to remove itGET /api/alerts/deadman/status— live watchdog health, monitor-loop progress, sanitized delivery failure state, and the most recent restart interruption; never includes the URL or endpoint fingerprintPOST /api/alerts/activateenables notification delivery and can notify about existing unacknowledged critical alerts. It is not a maintenance toggle, and there is no matching/api/alerts/deactivateendpoint.GET /api/alerts/activeGET /api/alerts/delivery-diagnosis?alertIdentifier=<alert-id>(omitalertIdentifierto get the diagnosis array for every active alert)GET /api/alerts/events?alertIdentifier=<alert-id>&type=<event-type,...>&since=<RFC3339>&limit=<n>— append-only alert event log: lifecycle transitions and notification decisions, including suppressions with reasons; all parameters optional, newest firstGET /api/alerts/historyDELETE /api/alerts/historyGET /api/alerts/incidentsPOST /api/alerts/incidents/notePOST /api/alerts/bulk/acknowledgePOST /api/alerts/bulk/clearPOST /api/alerts/acknowledge(body:{ "alertIdentifier": "alert-id" })POST /api/alerts/unacknowledge(body:{ "alertIdentifier": "alert-id" })POST /api/alerts/snooze(body:{ "alertIdentifier": "alert-id", "until": "RFC3339 timestamp" }; pauses delivery and escalation for up to 30 days while monitoring continues)POST /api/alerts/unsnooze(body:{ "alertIdentifier": "alert-id" }; resumes normal policy without resolving the incident)POST /api/alerts/clear(body:{ "alertIdentifier": "alert-id" })
🛡️ Security
Security Status
GET /api/security/status
Returns authentication status, proxy auth state, and security posture flags.
Change Password
POST /api/security/change-password
{ "currentPassword": "old-pass", "newPassword": "new-pass" }
In Docker installs, the response includes a restart notice.
Token management is instance administration, so a token bound to a tenant
organization gets 403 on these routes. Among the tokens that remain,
API-token callers manage only the tokens in their own organizations: every
organization a target token is bound to must be one the caller is bound to.
Tokens from other organizations are left out of the list and answer 404 on
read, edit, delete and rotate. Only an instance-wide token (full access, bound
only to the default organization) can create, edit, rotate or delete another
instance-wide token. Browser sessions are not limited this way.
List API Tokens
GET /api/security/tokens
Create API Token
POST /api/security/tokens
{ "name": "ansible-script", "scopes": ["monitoring:read"] }
Edit API Token
PATCH /api/security/tokens/<id>
{ "scopes": ["monitoring:read", "settings:read"] }
Send scopes, name, or both; a body with neither returns 400. A rename-only
body such as { "name": "new-name" } leaves the scopes unchanged, and a blank
name returns 400. When scopes is sent it must contain at least one known
scope, and wildcard access ("*") cannot be combined with other scopes.
API-token callers that change scopes must already hold every existing scope on
the target token and every scope they request. The update takes effect on the
token's next request without changing its ID, secret, expiry, or organization
bindings.
Rotate API Token
POST /api/security/tokens/<id>/rotate
Replaces one token with a new one and returns { "token": "raw-token", "record": { ... } }
(the raw token is shown once). The new token keeps the old one's name, scopes,
organization bindings, metadata and expiry, and the old token stops working.
API-token callers must hold every scope on the target token.
Revoke Token
DELETE /api/security/tokens/<id>
Validate API Token (admin session or settings:write token)
POST /api/security/validate-token
{ "token": "raw-token" }
Returns { "valid": true, "message": "Token is valid" } for a known, unexpired token. An unknown or expired token returns HTTP 200 with "valid": false and "message": "Token is invalid". An empty token returns HTTP 400 with "valid": false and "message": "Token is required". When no API tokens are configured, the response is HTTP 200 with "valid": false and "message": "API token authentication is not configured". API-token callers need the settings:write scope; a token without it gets HTTP 403. Non-admin proxy-auth callers get HTTP 403. Requests over the rate limit get HTTP 429. A successful validation updates the token's last-used time. This endpoint is not gated by the Pro audit licence.
Recovery (Localhost or Recovery Token)
POST /api/security/recovery
Supports actions:
generate_token(localhost only)disable_authenable_auth
GET /api/security/recovery returns recovery mode status.
Reset Account Lockout (Admin or settings:write token)
POST /api/security/reset-lockout (admin, settings:write)
{ "identifier": "admin" }
Callers need an admin session, or an API token with the settings:write scope; a token without that scope gets 403 with error: "missing_scope". Identifier can be a username or IP address. An empty identifier returns 400 missing_identifier. Success returns { "success": true, "message": "Lockout reset for admin" }.
Regenerate API Token (Admin)
POST /api/security/regenerate-token (admin, settings:write)
Replaces every stored API token with one new full-access token. All other
tokens stop working. An API token may call this only if it has full-access
scope (*) and is not bound to a non-default organization; any other token
gets 403, and should use POST /api/security/tokens/{id}/rotate to replace
just itself.
Returns the new raw token (shown once) and updates stored hashes:
{
"success": true,
"token": "raw-token",
"deploymentType": "systemd",
"requiresRestart": false,
"message": "New API token generated and active immediately! Save this token - it won't be shown again."
}
🧾 Audit Log (Pro)
These endpoints require admin access and the audit:read scope. On Community (no audit_logging license), these endpoints return HTTP 402 Payment Required with error: "license_required" and feature: "audit_logging" before any events are read.
List Audit Events
GET /api/audit?limit=100&event=login&user=admin&success=true&startTime=2024-01-01T00:00:00Z&endTime=2024-01-31T23:59:59Z
Response:
{
"events": [
{
"id": "6b3c9c3c-9a2f-4b3c-9a3b-3d0e8c5c5d45",
"timestamp": "2024-01-12T10:15:30Z",
"event": "login",
"user": "admin",
"ip": "198.51.100.10",
"path": "/api/login",
"success": true,
"details": "Successful login",
"signature": "..."
}
],
"total": 1,
"persistentLogging": true
}
Verify Audit Event Signature
GET /api/audit/<id>/verify
Response:
{
"available": true,
"verified": true,
"message": "Event signature verified"
}
Bootstrap Token Validation (Public)
POST /api/security/validate-bootstrap-token
Provide the token via header X-Setup-Token or JSON body:
{ "token": "bootstrap-token" }
Returns 204 No Content on success.
Quick Security Setup (Public, bootstrap token required)
POST /api/security/quick-setup
Requires a valid bootstrap token (header X-Setup-Token) or an authenticated session.
When authentication is already configured, a request that would change credentials
(force: true) from an API token needs an instance-wide full-access token; any
other token gets 403.
{
"username": "admin",
"password": "StrongPass!1",
"apiToken": "token",
"enableNotifications": false,
"darkMode": false,
"force": false,
"setupToken": "optional-bootstrap-token"
}
Apply Security Restart (Systemd Only)
POST /api/security/apply-restart
Applies auth changes by restarting the service (systemd deployments only).
⚙️ System Settings
Get Settings
GET /api/system/settings
Retrieve current system settings.
Update Settings
POST /api/system/settings/update
Update system settings. Requires admin + settings:write.
Legacy System Settings (Read Only)
GET /api/config/system
Legacy system settings endpoint (read-only).
Toggle Mock Mode
GET /api/system/mock-mode
POST /api/system/mock-mode
PUT /api/system/mock-mode
Enable or disable mock data generation (dev/demo only).
SSH Config (Temperature Monitoring)
POST /api/system/ssh-config
Writes the SSH config used for temperature collection (requires setup token or auth).
Verify Temperature SSH
POST /api/system/verify-temperature-ssh
Tests SSH connectivity for temperature collection (requires setup token or auth).
Scheduler Health
GET /api/monitoring/scheduler/health
Returns scheduler health, DLQ, and breaker status. Requires authentication (session or API token); no specific scope is enforced.
Updates (Admin)
GET /api/updates/checkPOST /api/updates/applyGET /api/updates/statusGET /api/updates/streamGET /api/updates/plan?version=X.Y.Z(optionalchannel, acceptsvprefix)GET /api/updates/historyGET /api/updates/history/entry?id=<event_id>GET /api/updates/pro-runtime(settings:read): whether this community build can switch to the Pulse Pro runtime. The license server decides eligibility;reasonexplains a switch that is not offered (not_activated,not_eligible,older_release,unavailable, ...). Docker installs get the digest-pinned image commands indocker.POST /api/updates/pro-runtime/apply(settings:write): installs the Pulse Pro runtime in place through the same verified, backed-up apply as an update (systemd and Proxmox LXC installs only). Returns409when no in-place switch is offered.
Infrastructure Updates
GET /api/infra-updates(requiresmonitoring:read)GET /api/infra-updates/summary(requiresmonitoring:read)POST /api/infra-updates/check(requiresmonitoring:write)GET /api/infra-updates/agent/{agentId}(requiresmonitoring:read)GET /api/infra-updates/{resourceId}(requiresmonitoring:read)
Diagnostics
GET /api/diagnostics(admin,settings:read)POST /api/diagnostics/docker/prepare-token(admin,settings:write)
The Docker migration token request accepts agentId, optional tokenName, and
optional enableHost. When enableHost is omitted, the generated token and
install command enable both host and Docker monitoring so the machine appears
in both Hosts and Docker. Set enableHost to false only for an intentional
workload-only agent; that mode receives Docker-report scope only.
Logs (Admin)
GET /api/logs/stream(server-sent stream)GET /api/logs/download(bundled logs)GET /api/logs/levelPOST /api/logs/level(set log level)
Server Info
GET /api/server/info
Returns minimal server info for installer scripts.
🔑 OIDC / SSO
Provider Login
GET /api/oidc/{providerID}/loginGET /api/oidc/{providerID}/callback
OIDC and SAML configuration is managed via SSO providers.
SSO Provider Management (Community)
GET /api/security/sso/providers(admin,settings:read)POST /api/security/sso/providers(admin,settings:write)GET /api/security/sso/providers/{id}(admin,settings:read)PUT /api/security/sso/providers/{id}(admin,settings:write)DELETE /api/security/sso/providers/{id}(admin,settings:write)
Provider mutation request contract:
- Max request body: 1MB.
- Unknown JSON fields are ignored. A trailing payload after the JSON object is rejected with
invalid_json. - Provider IDs must match server validation.
- OIDC and SAML providers are included with the Community SSO entitlement.
SSO Test and Metadata Preview (Community)
POST /api/security/sso/providers/test(admin,settings:write)POST /api/security/sso/providers/metadata/preview(admin,settings:read)
Test/preview request contract:
- Max request body: 32KB.
- Unknown JSON fields are ignored. A trailing payload after the JSON object is rejected with
invalid_json. - Common errors include
invalid_json,validation_error,rate_limited. A body over the 32KB limit is silently truncated, so it fails asinvalid_json(these endpoints do not returnbody_too_large).
💳 License (Relay / Pro / legacy Pro+ / Cloud)
License Status (Admin)
GET /api/license/status
License Features (Authenticated)
GET /api/license/features
Activate License (Admin, settings:write)
POST /api/license/activate
{ "license_key": "PASTE_KEY_HERE" }
Clear License (Admin, settings:write)
POST /api/license/clear
👥 RBAC / Role Management (Pro)
Role-based access control endpoints for managing roles and user assignments. Requires admin access and the rbac license feature.
List Roles
GET /api/admin/roles
Returns all defined roles.
Create Role
POST /api/admin/roles
{
"id": "operator",
"name": "Operator",
"description": "Can view and manage alerts",
"permissions": [
{ "action": "read", "resource": "alerts" },
{ "action": "write", "resource": "alerts" }
]
}
Update Role
PUT /api/admin/roles/{id}
Update an existing role's name, description, or permissions.
Delete Role
DELETE /api/admin/roles/{id}
List Users
GET /api/admin/users
Returns all users with their role assignments and, when available, mutable SSO presentation fields (displayName, email, providerType, providerId, and lastLoginAt). The opaque username remains the stable authorization principal.
Set User Roles
PUT /api/admin/users/{username}/roles
{ "roleIds": ["operator", "viewer"] }
Remove User Access
DELETE /api/admin/users/{username}
Deletes the Pulse RBAC identity and all assignments and revokes its active sessions. Administrators cannot remove their own current identity. This does not disable the upstream IdP account; a later authorized SSO login recreates the Pulse user record.
Note
: OIDC group-to-role mapping can automatically assign roles on login. See OIDC.md for configuration.
🏢 Organizations (Enterprise)
Multi-tenant organization management. Requires PULSE_MULTI_TENANT_ENABLED=true and an Enterprise license with the multi_tenant feature. All endpoints require authentication.
See MULTI_TENANT.md for setup and architecture details.
List Organizations
GET /api/orgs (requires settings:read)
Returns organizations accessible to the authenticated user.
Create Organization
POST /api/orgs (requires settings:write, session auth only)
{ "id": "acme-corp", "displayName": "Acme Corporation" }
The creator becomes the owner and first member. Organization IDs use letters, digits, periods, underscores or hyphens, 1–64 characters, but cannot be . or ... Prefer a simple lowercase/hyphen ID such as the example.
Get Organization
GET /api/orgs/{id} (requires settings:read)
Returns organization details. User must be a member.
Update Organization
PUT /api/orgs/{id} (requires settings:write, session auth only)
{ "displayName": "Updated Name" }
Admin or owner role required. The default organization cannot be updated.
Delete Organization
DELETE /api/orgs/{id} (requires settings:write, session auth only)
Admin or owner role required. The default organization cannot be deleted.
List Members
GET /api/orgs/{id}/members (requires settings:read)
Returns all members with their roles. User must be a member of the org.
Add or Update Member
POST /api/orgs/{id}/members (requires settings:write, session auth only)
{ "userId": "jane", "role": "editor" }
Roles: owner, admin, editor, viewer. Admin or owner role required. A new user receives a pending invitation (202) and must accept it in Pulse before gaining membership. Posting an existing member's userId updates their role; there is no member PATCH endpoint. Setting role to owner transfers ownership only to an existing member, by the current owner after fresh sign-in. Default org members cannot be managed.
Remove Member
DELETE /api/orgs/{id}/members/{userId} (requires settings:write, session auth only)
Admin or owner role required. The organization owner cannot be removed.
List Outgoing Shares
GET /api/orgs/{id}/shares (requires settings:read)
Returns resources shared outbound from this organization to others.
List Incoming Shares
GET /api/orgs/{id}/shares/incoming (requires settings:read)
Returns resources shared inbound to this organization from other organizations.
Create Share
POST /api/orgs/{id}/shares (requires settings:write, session auth only)
{
"targetOrgId": "partner-org",
"resourceType": "vm",
"resourceId": "vm-101",
"resourceName": "Web Server",
"accessRole": "viewer"
}
Share a resource with another organization, using the resource type and ID returned by Pulse. Supported types include vm, system-container, agent, node, docker-host, storage, pbs and pmg; generic host and container types are not supported. Access roles: viewer, editor, admin. Admin or owner role required on the source org. The share is pending until a target-org admin or owner accepts it in Pulse; changing its access role requires acceptance again.
Delete Share
DELETE /api/orgs/{id}/shares/{shareId} (requires settings:write, session auth only)
Revoke a resource share. Admin or owner role required.
🤖 Pulse Intelligence
Paid gating: endpoints labeled with a paid plan require the relevant Relay, Pro, legacy Pro+, or Cloud capability and return 402 Payment Required if the feature is not licensed.
Get AI Settings
GET /api/settings/ai
Returns current AI configuration (providers, models, patrol status). Requires admin + settings:read.
Update AI Settings
PUT /api/settings/ai/update (or POST /api/settings/ai/update)
Configure AI providers, API keys, and preferences. Requires admin + settings:write.
List Models
GET /api/ai/models
Lists models available to the configured providers (queried live from provider APIs).
Provider Tests (Admin)
POST /api/ai/testPOST /api/ai/test/{provider}
Legacy Anthropic OAuth Cleanup
Anthropic subscription OAuth is unsupported. These routes remain only for fail-closed compatibility and token cleanup:
POST /api/ai/oauth/start(admin): returns501withunsupported_anthropic_oauthPOST /api/ai/oauth/exchange(admin): returns501withunsupported_anthropic_oauthGET /api/ai/oauth/callback(public): redirects to settings withai_oauth_error=unsupportedunless the provider supplied a specific errorPOST /api/ai/oauth/disconnect(admin): clears stored legacy OAuth tokens
Execute (Chat + Tools)
POST /api/ai/execute
Runs an AI request which may return tool calls, findings, or suggested actions.
Execute (Streaming)
POST /api/ai/execute/stream
Streaming variant of execute (used by the UI for incremental responses).
Assistant Chat & Sessions
GET /api/ai/statusPOST /api/ai/chat(streaming)GET /api/ai/sessionsreturns session summaries; scoped Assistant handoffs may include a safehandoff_summarymarker without model-only context text or command payloads.POST /api/ai/sessionsDELETE /api/ai/sessions/{id}GET /api/ai/sessions/{id}/messagesPOST /api/ai/sessions/{id}/abortPOST /api/ai/sessions/{id}/summarizePOST /api/ai/sessions/{id}/fork- Legacy OpenCode-style file-change routes (
GET /api/ai/sessions/{id}/diff,POST /api/ai/sessions/{id}/revert,POST /api/ai/sessions/{id}/unrevert) return501 Not Implemented; Pulse Assistant sessions do not own file diffs or file-level revert.
Question Answers
POST /api/ai/question/{id}/answer
Kubernetes AI Analysis (Compatibility)
POST /api/ai/kubernetes/analyze
{ "cluster_id": "cluster-id" }
Requires Pro, legacy Pro+, or Cloud with the kubernetes_ai feature enabled. This route remains
available for compatibility, but current v6 Pulse Pro marketing does not treat
Kubernetes-specific analysis as a standalone plan pillar.
Alert Investigation (Pro)
POST /api/ai/investigate-alert
Runs a focused investigation for an alert payload (used by the UI).
Patrol
GET /api/ai/patrol/digest- Returns the "what Patrol did for you" rollup for the last
daysdays (querydays, 1–30, default 7): the window and whether retained run history covers it, the effective Patrol mode, runs (total, scheduled, event-triggered, manual, failed, checks, resources covered, last run), findings (new, still open by severity, resolved, auto-resolved, dismissed, suppressed), investigations by outcome, Patrol-origin actions (proposed, approved, rejected, executed, verified, failed, pending), alerts Patrol reviewed, and estimated model spend with a pricing-known flag. - Computed from records Pulse already retains (run history, the findings
store, canonical action audits, and usage cost events); nothing new is
persisted. Requires the
ai:executescope. Backs the Patrol page's "This week" card; seedocs/PATROL_WEEKLY_DIGEST.md.
- Returns the "what Patrol did for you" rollup for the last
GET /api/ai/patrol/attention- Returns the typed Patrol attention queue projected from canonical operational lifecycle records. This is the active-count and queue source used by both navigation and Patrol.
- Query params:
filter(active|open|acknowledged|suppressed|stale_unknown|resolved|all),page(minimum 1), andlimit(1–200). - The response includes
data, a lifecycle-widesummary, and bounded paginationmeta. Protection context is joined in one bounded batch, not fetched per item.
GET /api/ai/patrol/attention/summary- Returns the canonical active, open, acknowledged, suppressed,
stale/unknown, and recent-resolved counts plus
calm,coverageState, andevaluatedAt. - A lifecycle-read failure returns a typed unavailable error. It never returns a synthetic zero or healthy state.
- Returns the canonical active, open, acknowledged, suppressed,
stale/unknown, and recent-resolved counts plus
GET /api/ai/patrol/attention/{id}- Returns one attention item with its operational record, lifecycle timeline, typed evidence, recommended next step, relationships, and protection posture.
GET /api/ai/patrol/attention/{id}/evidence/{evidenceId}- Returns one exact retained evidence envelope with its current freshness.
- Returns
410 attention_evidence_detail_expiredwhen the operational record still links the ID but the bounded detail has expired.
POST /api/ai/patrol/attention/{id}/acknowledgePOST /api/ai/patrol/attention/{id}/unacknowledgePOST /api/ai/patrol/attention/{id}/suppress- Body:
{ "reason": "...", "expiresAt": "<RFC3339>" }. - The expiry must be in the future and no more than 30 days away.
- Body:
POST /api/ai/patrol/attention/{id}/unsuppressPOST /api/ai/patrol/attention/{id}/actions/restart/plan- Creates or replays the one server-owned Docker restart plan attached to this operational record and its exact evidence IDs.
- Requires the canonical action authorization, an eligible server-side
offer, and the Pulse Pro
ai_autofixentitlement. Clients cannot supply command authority or override the target.
- Attention reads require
monitoring:read; lifecycle mutations requiremonitoring:write. Action decision and execution use/api/actionsand retain their existing action-specific scopes. - Attention IDs and evidence IDs are opaque and can contain slashes. Clients must path-escape each ID.
GET /api/ai/patrol/autonomyPUT /api/ai/patrol/autonomyGET /api/ai/patrol/statusGET /api/ai/patrol/findingsDELETE /api/ai/patrol/findings(clear all findings)GET /api/ai/patrol/objectivesPOST /api/ai/patrol/objectivesGET /api/ai/patrol/objectives/{id}PATCH /api/ai/patrol/objectives/{id}DELETE /api/ai/patrol/objectives/{id}?revision={revision}- Objective writes accept an outcome-oriented
brief, optionaloptional_context, and optional canonicalresource_ids.PATCHalso acceptsstatus(active,paused, orarchived) and requires the currentrevisionin its JSON body. - Responses include server-derived
coverage(covered,degraded, oruncovered) and observer lifecycle state. Clients cannot submit either field or mark an objective covered. A saved objective remains uncovered until core records a validated, installed read-only observer with a live health lease. - Reads and writes require
ai:execute. Stale writes return409 patrol_objective_revision_conflict.
- Objective writes accept an outcome-oriented
GET /api/ai/patrol/historyGET /api/ai/patrol/runsGET /api/ai/patrol/runs/{id}(ai:execute)- Returns one retained Patrol run by the
idfromGET /api/ai/patrol/runs. Percent-encode the ID in the path. Tool-call arrays are omitted unless?include=tool_calls. Returns400when the ID is empty,404(not_found) when no retained run has that ID, and503(service_unavailable) when the Patrol service is unavailable.
- Returns one retained Patrol run by the
GET /api/ai/patrol/stream(ai:execute)- Server-sent events for live Patrol progress. Send
Last-Event-ID(or thelast_event_idquery parameter) to resume after the last eventid; replay is best-effort.
- Server-sent events for live Patrol progress. Send
POST /api/ai/patrol/run(admin,ai:execute)- Community installs are limited to one successful full run per hour and
get
429 patrol_rate_limiteduntil it has passed. Pro and Cloud have no cap.
- Community installs are limited to one successful full run per hour and
get
POST /api/ai/patrol/acknowledge(ai:execute)POST /api/ai/patrol/dismissPOST /api/ai/patrol/findings/notePOST /api/ai/patrol/resolvePOST /api/ai/patrol/snooze(ai:execute)POST /api/ai/patrol/suppress(ai:execute)GET /api/ai/patrol/suppressions(ai:execute)POST /api/ai/patrol/suppressions(ai:execute)DELETE /api/ai/patrol/suppressions/{id}(ai:execute)GET /api/ai/patrol/dismissed(ai:execute)
Patrol itself and managing its findings (acknowledge, snooze, suppress,
dismiss) are available on Community. Only the paid-labelled routes below
return 402. Tokens issued for Pulse Mobile (relay:mobile:access) are also
accepted on the acknowledge, dismiss and snooze routes.
Findings & Investigations
GET /api/ai/unified/findingsGET /api/ai/findings/{id}/investigationGET /api/ai/findings/{id}/investigation/messagesPOST /api/ai/findings/{id}/reinvestigate(Pro,ai:execute)- Needs the
ai_autofixentitlement (402 license_requiredotherwise) and a Patrol mode other than Watch only (400 autonomy_disabled). Returns409 investigation_runningwhile one is already in progress.
- Needs the
POST /api/ai/findings/{id}/reapprove(Pro,ai:execute; retired: it always returns410 Gonewith error codecommand_fix_retired; approve typed actions throughPOST /api/actions/{id}/decision(actions:approve) instead)
Approvals & Command Execution (Pro)
GET /api/ai/approvalsGET /api/ai/approvals/{id}POST /api/ai/approvals/{id}/approvePOST /api/ai/approvals/{id}/denyPOST /api/ai/run-command(retired: always returns410 Gonewith error coderaw_command_retired; raw command execution is no longer supported, so use advertised typed resource actions instead)GET /api/ai/agents(connected agents via/api/agent/ws)
Remediation Plans (Pro)
GET /api/ai/remediation/plansGET /api/ai/remediation/plan?plan_id=<id>POST /api/ai/remediation/approve(ai:execute; retired: on Pro builds it always returns410 Gonewith error codecommand_remediation_retiredafter the licence, method and scope checks, and community builds return a licence-required response; approve typed actions throughPOST /api/actions/{id}/decision(actions:approve) instead)POST /api/ai/remediation/execute(admin,ai:execute; retired: on Pro builds it always returns410 Gonewith error codecommand_remediation_retiredafter the licence, method and scope checks, and community builds return a licence-required response)POST /api/ai/remediation/rollback(admin,ai:execute; retired: on Pro builds it always returns410 Gonewith error codecommand_remediation_retiredafter the licence, method and scope checks, and community builds return a licence-required response; no rollback runs through this endpoint)
Intelligence & Forecasting
GET /api/ai/intelligenceGET /api/ai/intelligence/patternsGET /api/ai/intelligence/predictionsGET /api/ai/intelligence/correlationsGET /api/ai/intelligence/changes(canonical unified-resource timeline first, patrol-local memory fallback)GET /api/ai/intelligence/baselinesGET /api/ai/intelligence/remediationsGET /api/ai/intelligence/anomaliesGET /api/ai/intelligence/learningGET /api/ai/forecast(params:resource_id,metric, optionalresource_name,horizon_hours,threshold)GET /api/ai/forecasts/overview(params:metric,horizon_hours,threshold)GET /api/ai/learning/preferences(optionalresource_id)GET /api/ai/proxmox/eventsGET /api/ai/proxmox/correlationsGET /api/ai/incidents(optionalresource_id,limit)GET /api/ai/incidents/{resourceId}(optionallimit)GET /api/ai/circuit/status
Knowledge Base
GET /api/ai/knowledge?guest_id=<id>POST /api/ai/knowledge/savePOST /api/ai/knowledge/deleteGET /api/ai/knowledge/export?guest_id=<id>POST /api/ai/knowledge/importPOST /api/ai/knowledge/clear
Debug
GET /api/ai/debug/context(admin)
Cost Tracking
GET /api/ai/cost/summaryGET /api/ai/patrol/cost-preview(optionalmodelasprovider:model,interval_minutes; projects Patrol's 30-day cost for a model and schedule from Pulse's price table and the install's run history, with the per-run token assumption, 30-day spend against budget, and a recommended schedule)GET /api/ai/patrol/model-guidance(recommended / suggested / caution markers for the Patrol model pickers, plus this install's cached readiness pass)POST /api/ai/cost/reset(admin)GET /api/ai/cost/export(admin)
📈 Metrics Store
Auth required: monitoring:read.
Store Stats
GET /api/metrics-store/stats
Returns stats for the persistent metrics store (SQLite-backed).
History
GET /api/metrics-store/history
Returns historical metric series for a resource and time range.
Query params:
resourceType(required):node,storage,agent,disk,k8s,vm,system-container,oci-container,app-container,docker-hostresourceId(required)metric(optional):cpu,memory,disk, etc. Omit for all metricsrange(optional):1h,6h,12h,24h,1d,7d,30d,90d(default24h; duration strings also accepted)maxPoints(optional): Downsample to a target number of points
License: Requests beyond Community's
7dfloor require the paidlong_term_metricsentitlement. Relay unlocks14d, Pro and legacy Pro+ unlock90d, and requests beyond the active tier's limit return402 Payment Required. An explicitmetricreturns apointsarray; omitting it returns ametricsobject keyed by metric name. Use the source ID and type from the affected chart's request, not its display name or an internal store type. Oldercontainer,dockerHost,dockerContainer,guestanddockerquery values are unsupported. See Metrics History for private one-shot reads, response sources and empty-history troubleshooting.
🤖 Agent Endpoints
Unified Agent (Recommended)
GET /download/pulse-agent
Downloads the unified agent binary. Without arch, Pulse serves the local binary on the server host.
Optional query:
?arch=linux-amd64(supported:linux-amd64,linux-arm64,linux-armv7,linux-armv6,linux-386,darwin-amd64,darwin-arm64,freebsd-amd64,freebsd-arm64,windows-amd64,windows-arm64,windows-386)
The response includes X-Checksum-Sha256 for verification.
The unified agent combines host, Docker, and Kubernetes monitoring. Use --enable-docker or --enable-kubernetes to enable additional metrics.
See UNIFIED_AGENT.md for installation instructions.
Agent Version
GET /api/agent/version
Returns the current server version for agent update checks.
Agent Fleet Diagnostics
GET /api/agents/diagnostics (admin, settings:read)
Returns read-only fleet triage for reported host, Docker / Podman, and Kubernetes agents, including liveness, version drift, profile deployment drift, identity-split evidence, and supported repair handoff hints. It does not enqueue remote actions.
Unified Agent Installer Script
GET /install.sh
Serves the universal install.sh used to install pulse-agent on target machines.
Unified Agent Installer (Windows)
GET /install.ps1
Serves the PowerShell installer for Windows.
Submit Reports
POST /api/agents/agent/report - Agent metrics
POST /api/agents/docker/report - Docker container metrics
POST /api/agents/kubernetes/report - Kubernetes cluster metrics
Agent Management
GET /api/agents/agent/lookup?id=<agent_id>
GET /api/agents/agent/lookup?hostname=<hostname>
Looks up an agent by ID or hostname/display name. Requires agent:report.
POST /api/agents/agent/uninstall (agent:report token, or an admin session)
Agent self-unregister during uninstall: a token holding agent:report removes the agent that token owns. Without a token, only an admin session can remove an agent this way.
POST /api/agents/agent/unlink (admin, settings:write)
Unlinks an agent from a node.
DELETE /api/agents/agent/{agent_id} (admin, settings:write)
Removes an agent from state.
POST /api/agents/agent/{agent_id}/allow-reenroll (admin, settings:write)
Clears the removal block left by DELETE /api/agents/agent/{agent_id} so the agent can report again. Returns {"success": true, "agentId": "<agent_id>"}. Other methods return 405.
Agent Linking (Admin)
POST /api/agents/agent/link(admin,settings:write)POST /api/agents/agent/unlink(admin,settings:write)
Agent Remote Config
GET /api/agents/agent/{agent_id}/config
Returns the server-side config payload for an agent (used by remote config and debugging). Requires agent:config:read.
The config object includes the merged desired settings, command enablement
decision, and desired-config metadata. When signing is configured, the
signature remains backward-compatible with legacy agents: it covers
agentId, commandsEnabled, settings, issuedAt, and expiresAt.
desiredConfig is computed from the signed command decision plus the
agent-applied settings keys and returned as tamper-evident metadata for newer
clients to recompute and compare.
{
"success": true,
"agentId": "agent-123",
"config": {
"commandsEnabled": true,
"settings": {
"enable_docker": true
},
"desiredConfig": {
"version": "host-agent-config/v1",
"hash": "sha256:..."
},
"issuedAt": "2026-05-13T17:00:00Z",
"expiresAt": "2026-05-13T17:15:00Z",
"signature": "..."
}
}
PATCH /api/agents/agent/{agent_id}/config (admin, agent:manage)
Updates server-side config for an agent (e.g., commandsEnabled).
Docker / Podman Module Management (Admin)
These routes manage Docker / Podman telemetry and container actions reported by the Docker / Podman module inside the installed pulse-agent binary.
POST /api/agents/docker/commands/{commandId}/ack(docker:report)DELETE /api/agents/docker/runtimes/{agentId}(docker:manage, supports?hide=trueor?force=true)POST /api/agents/docker/runtimes/{agentId}/allow-reenroll(docker:manage)PUT /api/agents/docker/runtimes/{agentId}/unhide(docker:manage)PUT /api/agents/docker/runtimes/{agentId}/pending-uninstall(docker:manage)PUT /api/agents/docker/runtimes/{agentId}/display-name(docker:manage)POST /api/agents/docker/runtimes/{agentId}/check-updates(docker:manage)POST /api/agents/docker/runtimes/{agentId}/update-all(docker:manage) — retired, returns410 Gone; container updates run as reviewed per-container actions through/api/actionsPOST /api/agents/docker/containers/update(docker:manage) — retired, returns410 Gone; use the/api/actionsplan/decision/execute flow instead
Kubernetes Agent Management (Admin)
DELETE /api/agents/kubernetes/clusters/{clusterId}(kubernetes:manage) — removes the cluster and blocks re-enrollment untilallow-reenroll; takes nohideorforcequery parametersPOST /api/agents/kubernetes/clusters/{clusterId}/allow-reenroll(kubernetes:manage)PUT /api/agents/kubernetes/clusters/{clusterId}/unhide(kubernetes:manage)PUT /api/agents/kubernetes/clusters/{clusterId}/pending-uninstall(kubernetes:manage)PUT /api/agents/kubernetes/clusters/{clusterId}/display-name(kubernetes:manage)
Agent Profiles (Pro)
GET /api/admin/profiles/ (admin, settings:write, Pro)
POST /api/admin/profiles/ (admin, settings:write, Pro)
GET /api/admin/profiles/{id} (admin, settings:write, Pro)
PUT /api/admin/profiles/{id} (admin, settings:write, Pro)
DELETE /api/admin/profiles/{id} (admin, settings:write, Pro)
GET /api/admin/profiles/schema (admin, settings:write, Pro)
POST /api/admin/profiles/validate (admin, settings:write, Pro)
POST /api/admin/profiles/suggestions (admin, settings:write, Pro)
GET /api/admin/profiles/changelog (admin, settings:write, Pro)
GET /api/admin/profiles/deployments (admin, settings:write, Pro)
POST /api/admin/profiles/deployments (admin, settings:write, Pro)
GET /api/admin/profiles/{id}/versions (admin, settings:write, Pro)
POST /api/admin/profiles/{id}/rollback/{version} (admin, settings:write, Pro)
GET /api/admin/profiles/assignments (admin, settings:write, Pro)
POST /api/admin/profiles/assignments (admin, settings:write, Pro)
DELETE /api/admin/profiles/assignments/{agent_id} (admin, settings:write, Pro)
Every profile route requires admin access and the agent_profiles licence feature (HTTP 402 when unlicensed). Token callers also need settings:write on every profile route, including GET requests; session requests are not subject to the token scope check. Use the trailing slash on the collection path; the path without it is redirected, not served.
Availability Checks
Agentless availability checks monitor endpoint-only devices and services with ICMP ping, TCP port, UDP, HTTP, or HTTPS probes. They are managed from Settings -> Monitoring -> Availability checks and are also exposed through the API for automation.
Target Management
GET /api/availability-targets(admin,settings:read) - List configured targets and latest probe status.POST /api/availability-targets(admin,settings:write) - Add a target.PUT /api/availability-targets/{id}(admin,settings:write) - Update a target.DELETE /api/availability-targets/{id}(admin,settings:write) - Remove a target.POST /api/availability-targets/test(admin,settings:write) - Test an unsaved target. If the body includes theidof a saved target, that target's stored HTTP credentials are reused, but only while the HTTP scheme, host and port are unchanged.POST /api/availability-targets/{id}/test(admin,settings:write) - Test a saved target.
Target payload fields:
name- Display name.targetKind-machine,service, ordevice; defaults toservice.address- Hostname, IP address, or URL.protocol-icmp,tcp,udp,http, orhttps. The input aliaspingis accepted and is stored/returned as canonicalicmp.port- Required fortcp/udp, optional forhttp/https, and omitted foricmp.path- Optional HTTP path.udpMode- For UDP,response_required(default) oropen_or_filtered. The latter reports silence as indeterminate and fails only on an explicit rejection.udpRequest- UTF-8 request bytes, up to 512 bytes. Required byresponse_required; optional inopen_or_filteredmode.udpExpectedResponse- Optional exact UTF-8 response, up to 4096 bytes.enabled- Whether the target is scheduled.pollIntervalSeconds- Minimum 10 seconds; defaults to 60.timeoutMillis- Minimum 250 milliseconds; defaults to 2000.failureThreshold- Number of consecutive failures before alerting; defaults to 2.linkedResourceId- Optional resource id hint for attaching the probe facet to an existing resource.certificateMonitoringDisabled- Explicit HTTPS-only opt-out; certificate validity monitoring is enabled by default.certificateExpiryWarningDays- HTTPS certificate expiry warning window; defaults to 30 days.observationLocationIds- Observation locations for this one logical check:pulse:localfor this Pulse server andagent:<host-agent-id>for a connected agent. Agent locations require the Proexternal_probeentitlement. Includepulse:localonly when a local observation is wanted; agent-only sets do not also run locally. Each location retains separate evidence rather than treating one path failure as a universal outage.probeAgentId- Compatibility field for single-location clients. A non-empty registered host-agent ID selects that agent; an explicit empty string selects local execution. When updating only this field, omitobservationLocationIds. For multi-location updates, send the complete desiredobservationLocationIdsset and clearprobeAgentIdto""so a legacy value cannot override a single-location edit.
External agents collect observations, but alert evaluation and notification delivery still depend on the Pulse server. Missing reports are not proof of a target outage. See probe coverage and outage limits.
An explicit linkedResourceId is authoritative and fails closed when it
cannot resolve. Without it, Pulse correlates only on one exact normalized IP
or hostname match. Zero matches remain standalone and multiple matches remain
ambiguous; Pulse does not guess. Every configured target remains a distinct
network-endpoint in /api/resources, including correlated targets. A
correlated target also projects an additive availability /
availabilityChecks facet onto the matched resource and exposes an outgoing
checks relationship from the check resource. The check row remains the owner
of probe status, incidents, evidence, and history.
Example ping-only target:
{
"name": "Garage temperature sensor",
"targetKind": "device",
"address": "garage-sensor.local",
"protocol": "ping",
"enabled": true
}
The create response and subsequent reads return "protocol": "icmp".
History
POST /api/availability-history(monitoring:read) - Recent state and latency history for up to 200 availability targets in one request. Body:{"targetIds": ["<target-id>"], "range": "24h"}.targetIdsis required; blank and duplicate IDs are dropped, and at most 200 unique IDs are accepted.rangeis optional (default24h). Allowed ranges are1h,6h,12h,24h,7d,14d,30d,90d, and365d. A range longer than the licence's metrics history retention returns402(free tier: 7 days). Non-POST methods return405.
Response (200): start and end (UTC, end truncated to the minute) and targets. Each target has targetId and either summary (reachableSeconds, unreachableSeconds, indeterminateSeconds, unknownSeconds, coveragePercent, optional availabilityPercent, optional reachableLatencyMillis) with buckets (at most 120) and, only when present, revisionBoundaries; or an error with code not_found when the ID is not a configured target. An unknown ID does not fail the batch.
Errors: 400 with invalid_request (malformed JSON, unknown fields, or body over 32 KiB), missing_target_ids, too_many_target_ids, or invalid_range. Server-side failures return 500 or 503 with a typed error code.
🐟 TrueNAS
TrueNAS connection management endpoints for adding, testing, and removing TrueNAS SCALE/CORE instances.
Connection Management (Admin)
GET /api/truenas/connections(admin,settings:read) — List configured TrueNAS connections.POST /api/truenas/connections(admin,settings:write) — Add a new TrueNAS connection.POST /api/truenas/connections/test(admin,settings:write) — Test a TrueNAS connection before saving.POST /api/truenas/connections/{id}/test(admin,settings:write) — Live-test a saved connection with its stored credentials. An optional JSON body overrides fields for this test only and is not saved. Returns404(truenas_not_found) for an unknown ID and400(truenas_invalid_configortruenas_connection_failed) on failure. The probe does not clear poll errors or advance poll freshness, so check thepollfield fromGET /api/truenas/connectionsto see whether ordinary polling has recovered.DELETE /api/truenas/connections/{id}(admin,settings:write) — Remove a TrueNAS connection.PUT /api/truenas/connections/{id}(admin,settings:write) — Update a saved TrueNAS connection. Send only the fields to change in a private JSON file; omitted fields keep their stored values. A maskedapiKeyorpasswordreturned by a GET response is kept from the stored record, so never send a masked value as a new secret. Saving does not test the connection. SettingmonitorDatasets,monitorPoolsandmonitorReplicationall tofalseis saved as all enabled. Returns 200 with the redacted connection, 400validation_error, 404truenas_not_found, or 403mock_mode_enabledin mock mode.
TrueNAS resources (pools, datasets, disks, ZFS snapshots, replication tasks, alerts) are surfaced through the unified /api/resources endpoint with source=truenas.
📱 Relay / Pulse Mobile (retiring 31 March 2027)
End-to-end encrypted relay protocol for mobile connectivity.
Relay pairing endpoints generate the QR code and deep link used by supported Pulse Mobile clients.
Relay Configuration (Relay and Above; admin session or scoped token)
GET /api/settings/relay(settings:readtoken scope, or admin session, Relay+) — Get current relay configuration.PUT /api/settings/relay(settings:writetoken scope, or admin session, Relay+) — Update relay configuration.GET /api/settings/relay/status(settings:readtoken scope, or admin session, Relay+) — Get relay connection status.
Mobile Onboarding
GET /api/onboarding/qr(settings:read) — Generate QR code for Pulse Mobile pairing.POST /api/onboarding/validate(settings:read) — Validate a mobile onboarding connection.GET /api/onboarding/deep-link(settings:read) — Generate deep-link URL for mobile app.
🔌 WebSocket Endpoints
GET /ws– Primary UI WebSocket (browser sessions).GET /api/agent/ws– Agent WebSocket used for AI command execution.
Note
: This is a summary of the most common endpoints. For a complete list, inspect the network traffic of the Pulse dashboard or check the source code in
internal/api/router.go.