REST API¶
The full REST API surface, rendered from the OpenAPI 3.x spec at admin-openapi-v0.2.0.json.
For an interactive try it UI, point your browser at /swaggerui on a running admin — the spec there is always authoritative for that admin's deployed version.
Edge Admin OpenAPI 0.2.0¶
REST API for Edge Admin — the orchestration server for Edge Core.
Most endpoints require an API key (Authorization: Bearer <API_KEY>
or <MASTER_KEY>). A small set of bootstrap endpoints is intentionally
public — see the per-operation security field in the spec; an empty
array there means the endpoint is unauthenticated by design (e.g.
public enrollment-key creation).
Explore: - Swagger UI — interactive API explorer - ReDoc — reference documentation - Raw spec — OpenAPI JSON
Event streaming: Edge Admin also publishes lifecycle events to a message broker. See the AsyncAPI spec or download it.
Admins.Metadata¶
GET /api/v1/admins/me¶
Get the current admin
Description Returns the current admin's identity and configuration from metadata.
Input parameters
| Parameter | In | Type | Default | Nullable | Description |
|---|---|---|---|---|---|
| apiKey | header | string | N/A | No | API key for REST API access (API_KEY or MASTER_KEY) |
Response 200 OK¶
application/json
{
"data": {
"admin_cluster_name": "admin-cluster-1",
"admin_peer_count": 1,
"edge_node_capacity": 249,
"erlang_node_name": "admin@admin-k7m3n2p9x4j6.admin-cluster-1.nm.internal",
"id": "k7m3n2p9x4j6",
"last_computed_at": "2025-01-15T12:00:00Z",
"max_wireguard_peers": 250,
"name": "admin-k7m3n2p9x4j6",
"netmaker_host_id": "95e2707e-d11f-4551-bdd4-4ab2ab917505",
"vpn_hostname": "admin-k7m3n2p9x4j6.admin-cluster-1.nm.internal"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "Single admin identity response",
"properties": {
"data": {
"$ref": "#/components/schemas/Admin"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"data",
"meta"
],
"title": "AdminResponse",
"type": "object"
}
GET /api/v1/admins/my_admin_cluster¶
Get this admin's admin cluster
Description Returns metadata and peer topology for the admin cluster this admin belongs to.
Input parameters
| Parameter | In | Type | Default | Nullable | Description |
|---|---|---|---|---|---|
| apiKey | header | string | N/A | No | API key for REST API access (API_KEY or MASTER_KEY) |
Response 200 OK¶
application/json
{
"data": {
"degraded": false,
"name": "admin-cluster-1",
"topology": [
{
"admin_peer_count": 1,
"edge_node_capacity": 249,
"erlang_node_name": "admin@admin-k7m3n2p9x4j6.admin-cluster-1.nm.internal",
"max_wireguard_peers": 250,
"name": "admin-k7m3n2p9x4j6",
"netmaker_host_id": "95e2707e-d11f-4551-bdd4-4ab2ab917505",
"vpn_hostname": "admin-k7m3n2p9x4j6.admin-cluster-1.nm.internal"
},
{
"admin_peer_count": 1,
"edge_node_capacity": 249,
"erlang_node_name": "admin@admin-x9j4p2k7m8n3.admin-cluster-1.nm.internal",
"max_wireguard_peers": 250,
"name": "admin-x9j4p2k7m8n3",
"netmaker_host_id": "7f3c8d4e-9a1b-4c2d-8e3f-5a6b7c8d9e0f",
"vpn_hostname": "admin-x9j4p2k7m8n3.admin-cluster-1.nm.internal"
}
],
"total_admins": 2,
"total_edge_capacity": 498,
"total_nodes": 42,
"weak_leader": "admin-k7m3n2p9x4j6"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "Topology and state of the admin cluster this admin belongs to",
"properties": {
"data": {
"$ref": "#/components/schemas/MyAdminCluster"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"data",
"meta"
],
"title": "MyAdminClusterResponse",
"type": "object"
}
GET /api/v1/admins/admin_clusters¶
List all admin clusters from Netmaker
Description Lists every admin cluster Netmaker knows about, with each cluster's admins. Includes admins this instance is not a member of (cross-cluster visibility) and may include stale entries.
Input parameters
| Parameter | In | Type | Default | Nullable | Description |
|---|---|---|---|---|---|
| apiKey | header | string | N/A | No | API key for REST API access (API_KEY or MASTER_KEY) |
Response 200 OK¶
application/json
{
"data": [
{
"admin_count": 1,
"admins": [
{
"ipv4_address": "100.64.0.1",
"last_checked_in": "2026-04-28T12:34:56Z",
"name": "admin-7k3m9p2n",
"netmaker_host_id": "f272e703-b48f-4b61-b4c1-bfe4fffde62b",
"status": "online",
"use_static_port": true,
"vpn_hostname": "admin-7k3m9p2n.admin-cluster-main.nm.internal",
"wireguard_ip_address": "10.0.0.7",
"wireguard_port": 51820
}
],
"ipv4_range": "100.64.0.0/24",
"name": "admin-cluster-main"
}
],
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "All admin clusters Netmaker knows about",
"properties": {
"data": {
"$ref": "#/components/schemas/AdminClusters"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"data",
"meta"
],
"title": "AdminClustersResponse",
"type": "object"
}
Response 503 Service Unavailable¶
application/json
{
"error": {
"code": "service_unavailable",
"message": "Downstream dependency unreachable"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "503 Service Unavailable",
"example": {
"error": {
"code": "service_unavailable",
"message": "Downstream dependency unreachable"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "service_unavailable",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Downstream dependency unreachable",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "ServiceUnavailableResponse",
"type": "object"
}
GET /api/v1/admins/edge_clusters¶
Get all edge cluster assignments
Description Returns all edge cluster assignments across all admins from metadata
Input parameters
| Parameter | In | Type | Default | Nullable | Description |
|---|---|---|---|---|---|
| apiKey | header | string | N/A | No | API key for REST API access (API_KEY or MASTER_KEY) |
Response 200 OK¶
application/json
{
"data": {
"admin-k7m3n2p9x4j6": {
"cluster-p4k7n2m9x3j6": [
"node-uuid-x"
],
"cluster-x7j2p9k4m8n3": [
"node-uuid-1",
"node-uuid-2"
]
},
"admin-x9j4p2k7m8n3": {
"cluster-j6m8n3p7k2x4": [],
"cluster-m3n9p2k8x7j4": [
"node-uuid-3"
]
}
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "All edge cluster assignments across all admins",
"properties": {
"data": {
"$ref": "#/components/schemas/EdgeClusters"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"data",
"meta"
],
"title": "EdgeClustersResponse",
"type": "object"
}
GET /api/v1/admins/orphaned_clusters¶
Get all orphaned clusters
Description Returns all clusters that could not be assigned to any admin due to capacity constraints. Empty map when system is not degraded.
Input parameters
| Parameter | In | Type | Default | Nullable | Description |
|---|---|---|---|---|---|
| apiKey | header | string | N/A | No | API key for REST API access (API_KEY or MASTER_KEY) |
Response 200 OK¶
application/json
{
"data": {
"cluster-orphaned-1": [
"node-uuid-5",
"node-uuid-6"
],
"cluster-orphaned-2": [
"node-uuid-7"
]
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "Clusters with no assigned admin instance",
"properties": {
"data": {
"$ref": "#/components/schemas/OrphanedClusters"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"data",
"meta"
],
"title": "OrphanedClustersResponse",
"type": "object"
}
Admins.Metrics¶
GET /api/v1/admins/me/metrics¶
Get metrics for this admin
Description Returns application-level metrics from the edge_admin PromEx: - Application: uptime, BEAM stats (processes, memory, atoms, ETS tables) - Metadata: degraded status, orphaned/assigned clusters - Membership: step counts and full sequence completions - Discovery: peer discovery scan, DNS resolution, and connection counts - Nodes: health check statistics - Commands: delivery runs, per-execution delivery, completions, expirations - SSH: credential verification attempts and failures - Reconciliation: cluster Netmaker↔DB sync runs and errors - Self-updates: request processing completions - Gateways: connection events, active count, scrape totals - Event broker: publish/enqueue counters (zeroed when broker is disabled) - Oban: job queue states (available, scheduled, executing, etc.)
Input parameters
| Parameter | In | Type | Default | Nullable | Description |
|---|---|---|---|---|---|
| apiKey | header | string | N/A | No | API key for REST API access (API_KEY or MASTER_KEY) |
Response 200 OK¶
application/json
{
"data": {
"application": {
"atom_count": 38522,
"ets_count": 144,
"memory_atom_bytes": 1223807,
"memory_atom_mb": 1.17,
"memory_binary_bytes": 8817904,
"memory_binary_mb": 8.41,
"memory_code_bytes": 30935279,
"memory_code_mb": 29.5,
"memory_ets_bytes": 2884160,
"memory_ets_mb": 2.75,
"memory_processes_bytes": 25999000,
"memory_processes_mb": 24.79,
"memory_total_bytes": 101185152,
"memory_total_mb": 96.5,
"port_count": 31,
"process_count": 811,
"uptime_human": "23m",
"uptime_seconds": 1404
},
"commands": {
"delivery_delivered_count": 0,
"delivery_total": 48,
"execution_completed_total": 21,
"execution_delivered_total": 23,
"expiration_total": 2
},
"discovery": {
"dns_resolutions_total": 12,
"peer_connections_total": 3,
"scans_total": 144
},
"event_broker": {
"enabled": true,
"enqueues_total": 1235,
"publishes_error_total": 4,
"publishes_ok_total": 1230,
"publishes_total": 1234
},
"gateways": {
"active_count": 0,
"connections_total": 2,
"scrapes_total": 0
},
"membership": {
"complete_total": 1,
"steps_completed_total": 4
},
"metadata": {
"assigned_clusters": 0,
"degraded": false,
"orphaned_clusters": 0,
"recomputations_total": 26
},
"nodes": {
"health_checks_total": 480
},
"oban_queues": [
{
"available": 0,
"cancelled": 0,
"completed": 81,
"discarded": 0,
"executing": 0,
"queue": "zombie_admin_cleanup",
"retryable": 0,
"scheduled": 0
},
{
"available": 0,
"cancelled": 0,
"completed": 0,
"discarded": 0,
"executing": 0,
"queue": "execution_creation",
"retryable": 0,
"scheduled": 0
}
],
"proxy": {
"auth_failures_total": 34,
"bytes_down_mb": 9419.4,
"bytes_down_total": 9876543210,
"bytes_up_mb": 1452.9,
"bytes_up_total": 1523456789,
"connections_auth_failed_total": 34,
"connections_failure_total": 22,
"connections_success_total": 1189,
"connections_total": 1245,
"tunnels_closed_deadline_total": 38,
"tunnels_closed_drain_timeout_total": 11,
"tunnels_closed_normal_total": 1140,
"tunnels_closed_total": 1189
},
"quantum": {
"jobs_exceptions_total": 0,
"jobs_executed_total": 156
},
"reconciliation": {
"errors": 0,
"total": 12
},
"self_updates": {
"completed_total": 3
},
"ssh": {
"verifications_failed": 1,
"verifications_total": 34
},
"timestamp": "2025-12-26T15:30:00Z",
"vpn": {
"zombie_cleanup_deleted_count": 0,
"zombie_cleanup_total": 81
},
"webhook": {
"deliveries_ok_total": 2400,
"deliveries_recoverable_total": 60,
"deliveries_terminal_total": 10,
"deliveries_total": 2470,
"fan_outs_total": 1235
}
},
"meta": "Elixir.EdgeAdminWeb.Schemas.CommonSchemas.MetaSchema"
}
Schema of the response body
{
"description": "Application-level metrics from edge_admin PromEx (BEAM stats, metadata, Oban, etc.).\n",
"example": {
"data": {
"application": {
"atom_count": 38522,
"ets_count": 144,
"memory_atom_bytes": 1223807,
"memory_atom_mb": 1.17,
"memory_binary_bytes": 8817904,
"memory_binary_mb": 8.41,
"memory_code_bytes": 30935279,
"memory_code_mb": 29.5,
"memory_ets_bytes": 2884160,
"memory_ets_mb": 2.75,
"memory_processes_bytes": 25999000,
"memory_processes_mb": 24.79,
"memory_total_bytes": 101185152,
"memory_total_mb": 96.5,
"port_count": 31,
"process_count": 811,
"uptime_human": "23m",
"uptime_seconds": 1404
},
"commands": {
"delivery_delivered_count": 0,
"delivery_total": 48,
"execution_completed_total": 21,
"execution_delivered_total": 23,
"expiration_total": 2
},
"discovery": {
"dns_resolutions_total": 12,
"peer_connections_total": 3,
"scans_total": 144
},
"event_broker": {
"enabled": true,
"enqueues_total": 1235,
"publishes_error_total": 4,
"publishes_ok_total": 1230,
"publishes_total": 1234
},
"gateways": {
"active_count": 0,
"connections_total": 2,
"scrapes_total": 0
},
"membership": {
"complete_total": 1,
"steps_completed_total": 4
},
"metadata": {
"assigned_clusters": 0,
"degraded": false,
"orphaned_clusters": 0,
"recomputations_total": 26
},
"nodes": {
"health_checks_total": 480
},
"oban_queues": [
{
"available": 0,
"cancelled": 0,
"completed": 81,
"discarded": 0,
"executing": 0,
"queue": "zombie_admin_cleanup",
"retryable": 0,
"scheduled": 0
},
{
"available": 0,
"cancelled": 0,
"completed": 0,
"discarded": 0,
"executing": 0,
"queue": "execution_creation",
"retryable": 0,
"scheduled": 0
}
],
"proxy": {
"auth_failures_total": 34,
"bytes_down_mb": 9419.4,
"bytes_down_total": 9876543210,
"bytes_up_mb": 1452.9,
"bytes_up_total": 1523456789,
"connections_auth_failed_total": 34,
"connections_failure_total": 22,
"connections_success_total": 1189,
"connections_total": 1245,
"tunnels_closed_deadline_total": 38,
"tunnels_closed_drain_timeout_total": 11,
"tunnels_closed_normal_total": 1140,
"tunnels_closed_total": 1189
},
"quantum": {
"jobs_exceptions_total": 0,
"jobs_executed_total": 156
},
"reconciliation": {
"errors": 0,
"total": 12
},
"self_updates": {
"completed_total": 3
},
"ssh": {
"verifications_failed": 1,
"verifications_total": 34
},
"timestamp": "2025-12-26T15:30:00Z",
"vpn": {
"zombie_cleanup_deleted_count": 0,
"zombie_cleanup_total": 81
},
"webhook": {
"deliveries_ok_total": 2400,
"deliveries_recoverable_total": 60,
"deliveries_terminal_total": 10,
"deliveries_total": 2470,
"fan_outs_total": 1235
}
},
"meta": "Elixir.EdgeAdminWeb.Schemas.CommonSchemas.MetaSchema"
},
"properties": {
"data": {
"properties": {
"application": {
"description": "Application health and BEAM VM stats",
"properties": {
"atom_count": {
"description": "Number of allocated atoms",
"nullable": true,
"type": "integer"
},
"ets_count": {
"description": "Number of ETS tables",
"nullable": true,
"type": "integer"
},
"memory_atom_bytes": {
"description": "Memory used by atoms in bytes",
"nullable": true,
"type": "integer"
},
"memory_atom_mb": {
"description": "Memory used by atoms in MB",
"nullable": true,
"type": "number"
},
"memory_binary_bytes": {
"description": "Memory used by binaries in bytes",
"nullable": true,
"type": "integer"
},
"memory_binary_mb": {
"description": "Memory used by binaries in MB",
"nullable": true,
"type": "number"
},
"memory_code_bytes": {
"description": "Memory used by code in bytes",
"nullable": true,
"type": "integer"
},
"memory_code_mb": {
"description": "Memory used by code in MB",
"nullable": true,
"type": "number"
},
"memory_ets_bytes": {
"description": "Memory used by ETS tables in bytes",
"nullable": true,
"type": "integer"
},
"memory_ets_mb": {
"description": "Memory used by ETS tables in MB",
"nullable": true,
"type": "number"
},
"memory_processes_bytes": {
"description": "Memory used by processes in bytes",
"nullable": true,
"type": "integer"
},
"memory_processes_mb": {
"description": "Memory used by processes in MB",
"nullable": true,
"type": "number"
},
"memory_total_bytes": {
"description": "Total BEAM memory allocated in bytes",
"nullable": true,
"type": "integer"
},
"memory_total_mb": {
"description": "Total BEAM memory in MB",
"nullable": true,
"type": "number"
},
"port_count": {
"description": "Number of active ports",
"nullable": true,
"type": "integer"
},
"process_count": {
"description": "Number of BEAM processes running",
"nullable": true,
"type": "integer"
},
"uptime_human": {
"description": "Human-readable uptime (e.g., '2d 5h 30m')",
"nullable": true,
"type": "string"
},
"uptime_seconds": {
"description": "Application uptime in seconds",
"nullable": true,
"type": "integer"
}
},
"type": "object"
},
"commands": {
"description": "Command execution and delivery metrics",
"properties": {
"delivery_delivered_count": {
"description": "Number of executions queued for delivery in last batch run",
"nullable": true,
"type": "integer"
},
"delivery_total": {
"description": "Total delivery batch runs (Quantum scheduler cycles)",
"nullable": true,
"type": "integer"
},
"execution_completed_total": {
"description": "Total executions completed (result reported back by agent)",
"nullable": true,
"type": "integer"
},
"execution_delivered_total": {
"description": "Total individual execution delivery attempts to agents (success + failure)",
"nullable": true,
"type": "integer"
},
"expiration_total": {
"description": "Total stale execution expiration sweeps",
"nullable": true,
"type": "integer"
}
},
"type": "object"
},
"discovery": {
"description": "Peer admin discovery metrics",
"properties": {
"dns_resolutions_total": {
"description": "Total DNS resolution attempts during peer discovery",
"nullable": true,
"type": "integer"
},
"peer_connections_total": {
"description": "Total Erlang peer connection attempts (success + failure + already_connected)",
"nullable": true,
"type": "integer"
},
"scans_total": {
"description": "Total peer discovery scan cycles completed",
"nullable": true,
"type": "integer"
}
},
"type": "object"
},
"event_broker": {
"description": "Event broker publish metrics. When the broker is disabled (default), `enabled` is `false`\nand all counters are 0. A sustained gap between `enqueues_total` and `publishes_ok_total`\nindicates broker failures with events accumulating in Oban for retry.\n",
"properties": {
"enabled": {
"description": "Whether the event broker is enabled (mirrors EVENT_BROKER_ENABLED config)",
"nullable": true,
"type": "boolean"
},
"enqueues_total": {
"description": "Total events enqueued for async broker delivery (before any publish attempt)",
"nullable": true,
"type": "integer"
},
"publishes_error_total": {
"description": "Total broker publishes that failed (will be retried by Oban)",
"nullable": true,
"type": "integer"
},
"publishes_ok_total": {
"description": "Total broker publishes that succeeded",
"nullable": true,
"type": "integer"
},
"publishes_total": {
"description": "Total broker publish attempts (ok + error)",
"nullable": true,
"type": "integer"
}
},
"type": "object"
},
"gateways": {
"description": "Gateway connection and scrape metrics",
"properties": {
"active_count": {
"description": "Current number of active gateway connections",
"nullable": true,
"type": "integer"
},
"connections_total": {
"description": "Total gateway connection events (connects + disconnects)",
"nullable": true,
"type": "integer"
},
"scrapes_total": {
"description": "Total metrics scrape operations performed by gateways",
"nullable": true,
"type": "integer"
}
},
"type": "object"
},
"membership": {
"description": "Admin-cluster membership initialization metrics",
"properties": {
"complete_total": {
"description": "Total full membership sequences completed (success + failure)",
"nullable": true,
"type": "integer"
},
"steps_completed_total": {
"description": "Total individual membership steps completed across all restarts",
"nullable": true,
"type": "integer"
}
},
"type": "object"
},
"metadata": {
"description": "Admin metadata and cluster assignment status",
"properties": {
"assigned_clusters": {
"description": "Number of clusters assigned to this admin",
"nullable": true,
"type": "integer"
},
"degraded": {
"description": "Whether admin is in degraded state",
"nullable": true,
"type": "boolean"
},
"orphaned_clusters": {
"description": "Number of orphaned clusters detected",
"nullable": true,
"type": "integer"
},
"recomputations_total": {
"description": "Total metadata recomputations performed",
"nullable": true,
"type": "integer"
}
},
"type": "object"
},
"nodes": {
"description": "Node health check metrics",
"properties": {
"health_checks_total": {
"description": "Total node health checks performed",
"nullable": true,
"type": "integer"
}
},
"type": "object"
},
"oban_queues": {
"description": "Oban job queue states",
"items": {
"properties": {
"available": {
"description": "Jobs available to run",
"type": "integer"
},
"cancelled": {
"description": "Cancelled jobs",
"type": "integer"
},
"completed": {
"description": "Completed jobs",
"type": "integer"
},
"discarded": {
"description": "Discarded jobs (max retries exceeded)",
"type": "integer"
},
"executing": {
"description": "Jobs currently executing",
"type": "integer"
},
"queue": {
"description": "Queue name (e.g., 'zombie_admin_cleanup', 'execution_creation')",
"type": "string"
},
"retryable": {
"description": "Jobs awaiting retry",
"type": "integer"
},
"scheduled": {
"description": "Jobs scheduled for future execution",
"type": "integer"
}
},
"type": "object"
},
"type": "array"
},
"proxy": {
"description": "HTTP and SOCKS5 forward proxy metrics (connections, tunnels, bytes transferred)",
"properties": {
"auth_failures_total": {
"description": "Total proxy authentication failures (mirrors connections_auth_failed_total)",
"nullable": true,
"type": "integer"
},
"bytes_down_mb": {
"description": "Cumulative target→client bytes in MB",
"nullable": true,
"type": "number"
},
"bytes_down_total": {
"description": "Cumulative bytes forwarded target→client across all tunnels",
"nullable": true,
"type": "integer"
},
"bytes_up_mb": {
"description": "Cumulative client→target bytes in MB",
"nullable": true,
"type": "number"
},
"bytes_up_total": {
"description": "Cumulative bytes forwarded client→target across all tunnels",
"nullable": true,
"type": "integer"
},
"connections_auth_failed_total": {
"description": "Total proxy connections rejected at authentication",
"nullable": true,
"type": "integer"
},
"connections_failure_total": {
"description": "Total proxy connections that failed for non-auth reasons (protocol, network, gateway, etc.)",
"nullable": true,
"type": "integer"
},
"connections_success_total": {
"description": "Total proxy connections that authenticated and established a tunnel",
"nullable": true,
"type": "integer"
},
"connections_total": {
"description": "Total proxy connections seen (success + auth_failed + failure)",
"nullable": true,
"type": "integer"
},
"tunnels_closed_deadline_total": {
"description": "Tunnels force-closed by the total-duration deadline (slowloris defence)",
"nullable": true,
"type": "integer"
},
"tunnels_closed_drain_timeout_total": {
"description": "Tunnels force-closed after exceeding the graceful drain grace window",
"nullable": true,
"type": "integer"
},
"tunnels_closed_normal_total": {
"description": "Tunnels closed normally (both sides EOF'd cleanly)",
"nullable": true,
"type": "integer"
},
"tunnels_closed_total": {
"description": "Total tunnels that reached end-of-life (all close reasons combined)",
"nullable": true,
"type": "integer"
}
},
"type": "object"
},
"quantum": {
"description": "Quantum scheduler job execution metrics",
"properties": {
"jobs_exceptions_total": {
"description": "Total Quantum job exceptions/failures",
"nullable": true,
"type": "integer"
},
"jobs_executed_total": {
"description": "Total Quantum jobs executed across all job types",
"nullable": true,
"type": "integer"
}
},
"type": "object"
},
"reconciliation": {
"description": "Cluster reconciliation metrics (Netmaker ↔ DB sync)",
"properties": {
"errors": {
"description": "Number of errors in last reconciliation run",
"nullable": true,
"type": "integer"
},
"total": {
"description": "Total cluster reconciliation runs",
"nullable": true,
"type": "integer"
}
},
"type": "object"
},
"self_updates": {
"description": "Self-update request processing metrics",
"properties": {
"completed_total": {
"description": "Total self-update requests processed to completion",
"nullable": true,
"type": "integer"
}
},
"type": "object"
},
"ssh": {
"description": "SSH credential verification metrics",
"properties": {
"verifications_failed": {
"description": "Total SSH credential verification failures",
"nullable": true,
"type": "integer"
},
"verifications_total": {
"description": "Total SSH credential verification attempts (all auth methods)",
"nullable": true,
"type": "integer"
}
},
"type": "object"
},
"timestamp": {
"description": "When the metrics were collected (ISO 8601 format)",
"format": "date-time",
"type": "string"
},
"vpn": {
"description": "VPN management metrics",
"properties": {
"zombie_cleanup_deleted_count": {
"description": "Number of zombie admins deleted in last cleanup",
"nullable": true,
"type": "integer"
},
"zombie_cleanup_total": {
"description": "Total zombie admin cleanup runs",
"nullable": true,
"type": "integer"
}
},
"type": "object"
},
"webhook": {
"description": "Webhook delivery metrics. `fan_outs_total` counts publish-time fan-out invocations\n(one per published event regardless of how many webhooks match). `deliveries_*` count\nindividual HTTP delivery attempts and their outcomes — `ok`, `recoverable` (retried by\nOban: 408/429/503/network until `WEBHOOK_MAX_ATTEMPTS` is exhausted), `terminal`\n(cancelled by the worker, no further retries).\n",
"properties": {
"deliveries_ok_total": {
"description": "Total deliveries that returned 2xx",
"nullable": true,
"type": "integer"
},
"deliveries_recoverable_total": {
"description": "Total deliveries that hit a recoverable error (will be retried)",
"nullable": true,
"type": "integer"
},
"deliveries_terminal_total": {
"description": "Total deliveries that hit a terminal error (cancelled, contributes to auto-disable)",
"nullable": true,
"type": "integer"
},
"deliveries_total": {
"description": "Total webhook delivery attempts (ok + recoverable + terminal)",
"nullable": true,
"type": "integer"
},
"fan_outs_total": {
"description": "Total fan-out invocations from the publish path",
"nullable": true,
"type": "integer"
}
},
"type": "object"
}
},
"type": "object"
}
},
"required": [
"data",
"meta"
],
"title": "AdminMetricsResponse",
"type": "object"
}
Response 503 Service Unavailable¶
application/json
{
"error": {
"code": "service_unavailable",
"message": "Downstream dependency unreachable"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "503 Service Unavailable",
"example": {
"error": {
"code": "service_unavailable",
"message": "Downstream dependency unreachable"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "service_unavailable",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Downstream dependency unreachable",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "ServiceUnavailableResponse",
"type": "object"
}
Nodes.Cluster¶
POST /api/v1/clusters¶
Create a new cluster
Description
Create a new edge cluster with optional IP range. The name default is reserved (used as a URL keyword on convenience routes) and will be rejected with HTTP 422.
Note: This endpoint is unavailable during degraded mode (503).
Input parameters
| Parameter | In | Type | Default | Nullable | Description |
|---|---|---|---|---|---|
| apiKey | header | string | N/A | No | API key for REST API access (API_KEY or MASTER_KEY) |
Request body
application/json
This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.Schema of the request body
{
"description": "Parameters for creating a new cluster",
"example": {
"ipv4_range": "100.64.1.0/24",
"name": "prod-east",
"node_limit": 50
},
"properties": {
"ipv4_range": {
"description": "IPv4 CIDR range (auto-generated if not provided)",
"example": "100.64.0.0/24",
"nullable": true,
"pattern": "^\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\/\\d{1,2}$",
"type": "string"
},
"name": {
"description": "Cluster name — primary identifier (max 24 chars, lowercase alphanumeric + hyphens)",
"example": "prod-east",
"maxLength": 24,
"pattern": "^[a-z0-9]([a-z0-9-]*[a-z0-9])?$",
"type": "string"
},
"node_limit": {
"description": "Maximum nodes allowed in this cluster (null means no limit enforced)",
"example": 50,
"minimum": 1,
"nullable": true,
"type": "integer"
}
},
"required": [
"name"
],
"title": "ClusterCreateRequest",
"type": "object"
}
Response 201 Created¶
application/json
{
"data": {
"id": "abc12345-1234-1234-1234-123456789abc",
"inserted_at": "2025-06-09T08:00:00Z",
"ipv4_range": "100.64.0.0/24",
"name": "prod-east",
"network_name": "cluster-prod-east",
"node_count": 2,
"node_limit": 50,
"nodes": [
{
"id": "abc12345-1234-1234-1234-123456789abc",
"id_type": "persistent",
"status": "healthy",
"vpn_hostname": "node-abc12345-1234-1234-1234-123456789abc.cluster-prod-east.nm.internal"
},
{
"id": "def67890-5678-5678-5678-567890abcdef",
"id_type": "persistent",
"status": "healthy",
"vpn_hostname": "node-def67890-5678-5678-5678-567890abcdef.cluster-prod-east.nm.internal"
}
],
"updated_at": "2025-06-09T08:00:00Z",
"vpn_domain": "cluster-prod-east.nm.internal"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "Single cluster response",
"properties": {
"data": {
"$ref": "#/components/schemas/ClusterResponse"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"data",
"meta"
],
"title": "ClusterSingleResponse",
"type": "object"
}
Response 409 Conflict¶
application/json
{
"error": {
"code": "conflict",
"message": "Resource already exists"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "409 Conflict",
"example": {
"error": {
"code": "conflict",
"message": "Resource already exists"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "conflict",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Resource already exists",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "ConflictResponse",
"type": "object"
}
Response 422 Unprocessable Entity¶
application/json
{
"error": {
"code": "validation_failed",
"details": {
"name": [
"can't be blank"
],
"timeout": [
"must be greater than 0"
]
},
"message": "Validation failed"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "422 Validation Failed",
"example": {
"error": {
"code": "validation_failed",
"details": {
"name": [
"can't be blank"
],
"timeout": [
"must be greater than 0"
]
},
"message": "Validation failed"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "validation_failed",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Validation failed",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "ChangesetErrorResponse",
"type": "object"
}
Response 503 Service Unavailable¶
application/json
{
"error": {
"code": "service_unavailable",
"message": "Downstream dependency unreachable"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "503 Service Unavailable",
"example": {
"error": {
"code": "service_unavailable",
"message": "Downstream dependency unreachable"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "service_unavailable",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Downstream dependency unreachable",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "ServiceUnavailableResponse",
"type": "object"
}
GET /api/v1/clusters¶
List all clusters
Description Returns a paginated list of all edge clusters with filtering and sorting
Input parameters
| Parameter | In | Type | Default | Nullable | Description |
|---|---|---|---|---|---|
| apiKey | header | string | N/A | No | API key for REST API access (API_KEY or MASTER_KEY) |
| has_node_limit | query | boolean | No | Filter by whether a node limit is set: true returns clusters with a limit, false returns unlimited | |
| inserted_at__gte | query | No | Filter records where inserted_at is on or after this datetime (ISO 8601 datetime; date-only is treated as start of day UTC) | ||
| inserted_at__lte | query | No | Filter records where inserted_at is on or before this datetime (ISO 8601 datetime; date-only is treated as end of day UTC) | ||
| ipv4_range | query | string | No | Filter by IPv4 range (exact match or wildcard) | |
| name | query | string | No | Filter by cluster name (exact match or wildcard: prod, tion, rod) | |
| node_count__gte | query | integer | No | Filter by minimum node_count | |
| node_count__lte | query | integer | No | Filter by maximum node_count | |
| node_limit | query | integer | No | Filter by exact node limit | |
| node_limit__gte | query | integer | No | Filter by minimum node_limit | |
| node_limit__lte | query | integer | No | Filter by maximum node_limit | |
| order_by | query | string | No | Comma-separated list of fields to sort by | |
| order_directions | query | string | No | Comma-separated list of sort directions (asc/desc) corresponding to order_by fields | |
| page | query | integer | 1 | No | Page number (1-indexed) |
| page_size | query | integer | 20 | No | Items per page |
| updated_at__gte | query | No | Filter records where updated_at is on or after this datetime (ISO 8601 datetime; date-only is treated as start of day UTC) | ||
| updated_at__lte | query | No | Filter records where updated_at is on or before this datetime (ISO 8601 datetime; date-only is treated as end of day UTC) |
Response 200 OK¶
application/json
{
"data": [
{
"id": "abc12345-1234-1234-1234-123456789abc",
"inserted_at": "2025-06-09T08:00:00Z",
"ipv4_range": "100.64.0.0/24",
"name": "prod-east",
"network_name": "cluster-prod-east",
"node_count": 2,
"node_limit": 50,
"nodes": [
{
"id": "abc12345-1234-1234-1234-123456789abc",
"id_type": "persistent",
"status": "healthy",
"vpn_hostname": "node-abc12345-1234-1234-1234-123456789abc.cluster-prod-east.nm.internal"
},
{
"id": "def67890-5678-5678-5678-567890abcdef",
"id_type": "persistent",
"status": "healthy",
"vpn_hostname": "node-def67890-5678-5678-5678-567890abcdef.cluster-prod-east.nm.internal"
}
],
"updated_at": "2025-06-09T08:00:00Z",
"vpn_domain": "cluster-prod-east.nm.internal"
}
],
"meta": {
"pagination": {
"has_next": true,
"has_prev": false,
"next_page": 2,
"page": 1,
"page_size": 20,
"prev_page": 0,
"total_count": 150,
"total_pages": 8
},
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "Paginated list of clusters with metadata",
"properties": {
"data": {
"items": {
"$ref": "#/components/schemas/ClusterResponse"
},
"type": "array"
},
"meta": {
"$ref": "#/components/schemas/PaginatedMeta"
}
},
"required": [
"data",
"meta"
],
"title": "ClusterPaginatedResponse",
"type": "object"
}
Response 400 Bad Request¶
application/json
{
"error": {
"code": "bad_request",
"details": {
"ipv4_range": [
"Invalid format."
],
"name": [
"Invalid format. Expected ~r/^[a-z0-9]/"
]
},
"message": "Invalid request parameters"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "400 Bad Request",
"example": {
"error": {
"code": "bad_request",
"details": {
"ipv4_range": [
"Invalid format."
],
"name": [
"Invalid format. Expected ~r/^[a-z0-9]/"
]
},
"message": "Invalid request parameters"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "bad_request",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Invalid request parameters",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "BadRequestResponse",
"type": "object"
}
GET /api/v1/clusters/{name}¶
Get a specific cluster
Description Returns details for a specific cluster by name
Input parameters
| Parameter | In | Type | Default | Nullable | Description |
|---|---|---|---|---|---|
| apiKey | header | string | N/A | No | API key for REST API access (API_KEY or MASTER_KEY) |
| name | path | string | No | Cluster name |
Response 200 OK¶
application/json
{
"data": {
"id": "abc12345-1234-1234-1234-123456789abc",
"inserted_at": "2025-06-09T08:00:00Z",
"ipv4_range": "100.64.0.0/24",
"name": "prod-east",
"network_name": "cluster-prod-east",
"node_count": 2,
"node_limit": 50,
"nodes": [
{
"id": "abc12345-1234-1234-1234-123456789abc",
"id_type": "persistent",
"status": "healthy",
"vpn_hostname": "node-abc12345-1234-1234-1234-123456789abc.cluster-prod-east.nm.internal"
},
{
"id": "def67890-5678-5678-5678-567890abcdef",
"id_type": "persistent",
"status": "healthy",
"vpn_hostname": "node-def67890-5678-5678-5678-567890abcdef.cluster-prod-east.nm.internal"
}
],
"updated_at": "2025-06-09T08:00:00Z",
"vpn_domain": "cluster-prod-east.nm.internal"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "Single cluster response",
"properties": {
"data": {
"$ref": "#/components/schemas/ClusterResponse"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"data",
"meta"
],
"title": "ClusterSingleResponse",
"type": "object"
}
Response 400 Bad Request¶
application/json
{
"error": {
"code": "bad_request",
"details": {
"ipv4_range": [
"Invalid format."
],
"name": [
"Invalid format. Expected ~r/^[a-z0-9]/"
]
},
"message": "Invalid request parameters"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "400 Bad Request",
"example": {
"error": {
"code": "bad_request",
"details": {
"ipv4_range": [
"Invalid format."
],
"name": [
"Invalid format. Expected ~r/^[a-z0-9]/"
]
},
"message": "Invalid request parameters"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "bad_request",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Invalid request parameters",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "BadRequestResponse",
"type": "object"
}
Response 404 Not Found¶
application/json
{
"error": {
"code": "not_found",
"message": "Resource not found"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "404 Not Found",
"example": {
"error": {
"code": "not_found",
"message": "Resource not found"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "not_found",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Resource not found",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "NotFoundResponse",
"type": "object"
}
PATCH /api/v1/clusters/{name}¶
Update a cluster
Description Update a cluster's settings. Only provided fields are changed. Pass null to unset a nullable field.
Note: This endpoint is unavailable during degraded mode (503).
Input parameters
| Parameter | In | Type | Default | Nullable | Description |
|---|---|---|---|---|---|
| apiKey | header | string | N/A | No | API key for REST API access (API_KEY or MASTER_KEY) |
| name | path | string | No | Cluster name |
Request body
application/json
This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.Schema of the request body
{
"description": "Parameters for updating a cluster. Only provided fields are updated. Pass null to unset a nullable field.",
"example": {
"node_limit": 50
},
"properties": {
"node_limit": {
"description": "Maximum nodes allowed in this cluster (null means no limit enforced)",
"example": 50,
"minimum": 1,
"nullable": true,
"type": "integer"
}
},
"title": "ClusterUpdateRequest",
"type": "object"
}
Response 200 OK¶
application/json
{
"data": {
"id": "abc12345-1234-1234-1234-123456789abc",
"inserted_at": "2025-06-09T08:00:00Z",
"ipv4_range": "100.64.0.0/24",
"name": "prod-east",
"network_name": "cluster-prod-east",
"node_count": 2,
"node_limit": 50,
"nodes": [
{
"id": "abc12345-1234-1234-1234-123456789abc",
"id_type": "persistent",
"status": "healthy",
"vpn_hostname": "node-abc12345-1234-1234-1234-123456789abc.cluster-prod-east.nm.internal"
},
{
"id": "def67890-5678-5678-5678-567890abcdef",
"id_type": "persistent",
"status": "healthy",
"vpn_hostname": "node-def67890-5678-5678-5678-567890abcdef.cluster-prod-east.nm.internal"
}
],
"updated_at": "2025-06-09T08:00:00Z",
"vpn_domain": "cluster-prod-east.nm.internal"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "Single cluster response",
"properties": {
"data": {
"$ref": "#/components/schemas/ClusterResponse"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"data",
"meta"
],
"title": "ClusterSingleResponse",
"type": "object"
}
Response 404 Not Found¶
application/json
{
"error": {
"code": "not_found",
"message": "Resource not found"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "404 Not Found",
"example": {
"error": {
"code": "not_found",
"message": "Resource not found"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "not_found",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Resource not found",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "NotFoundResponse",
"type": "object"
}
Response 422 Unprocessable Entity¶
application/json
{
"error": {
"code": "validation_failed",
"details": {
"name": [
"can't be blank"
],
"timeout": [
"must be greater than 0"
]
},
"message": "Validation failed"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "422 Validation Failed",
"example": {
"error": {
"code": "validation_failed",
"details": {
"name": [
"can't be blank"
],
"timeout": [
"must be greater than 0"
]
},
"message": "Validation failed"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "validation_failed",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Validation failed",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "ChangesetErrorResponse",
"type": "object"
}
Response 503 Service Unavailable¶
application/json
{
"error": {
"code": "service_unavailable",
"message": "Downstream dependency unreachable"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "503 Service Unavailable",
"example": {
"error": {
"code": "service_unavailable",
"message": "Downstream dependency unreachable"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "service_unavailable",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Downstream dependency unreachable",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "ServiceUnavailableResponse",
"type": "object"
}
DELETE /api/v1/clusters/{name}¶
Delete a cluster
Description Delete an empty cluster (must have no nodes).
Note: This endpoint is unavailable during degraded mode (503).
Input parameters
| Parameter | In | Type | Default | Nullable | Description |
|---|---|---|---|---|---|
| apiKey | header | string | N/A | No | API key for REST API access (API_KEY or MASTER_KEY) |
| name | path | string | No | Cluster name |
Response 204 No Content¶
Schema of the response body
Response 400 Bad Request¶
application/json
{
"error": {
"code": "bad_request",
"details": {
"ipv4_range": [
"Invalid format."
],
"name": [
"Invalid format. Expected ~r/^[a-z0-9]/"
]
},
"message": "Invalid request parameters"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "400 Bad Request",
"example": {
"error": {
"code": "bad_request",
"details": {
"ipv4_range": [
"Invalid format."
],
"name": [
"Invalid format. Expected ~r/^[a-z0-9]/"
]
},
"message": "Invalid request parameters"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "bad_request",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Invalid request parameters",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "BadRequestResponse",
"type": "object"
}
Response 404 Not Found¶
application/json
{
"error": {
"code": "not_found",
"message": "Resource not found"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "404 Not Found",
"example": {
"error": {
"code": "not_found",
"message": "Resource not found"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "not_found",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Resource not found",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "NotFoundResponse",
"type": "object"
}
Response 409 Conflict¶
application/json
{
"error": {
"code": "conflict",
"message": "Resource already exists"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "409 Conflict",
"example": {
"error": {
"code": "conflict",
"message": "Resource already exists"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "conflict",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Resource already exists",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "ConflictResponse",
"type": "object"
}
Response 503 Service Unavailable¶
application/json
{
"error": {
"code": "service_unavailable",
"message": "Downstream dependency unreachable"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "503 Service Unavailable",
"example": {
"error": {
"code": "service_unavailable",
"message": "Downstream dependency unreachable"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "service_unavailable",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Downstream dependency unreachable",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "ServiceUnavailableResponse",
"type": "object"
}
Nodes.EnrollmentKey¶
POST /api/v1/clusters/default/enrollment_keys/public¶
Get a public enrollment key for the default cluster
Description Public endpoint (no authentication required). Only enabled when both PUBLIC_ENROLLMENT_KEY_ENABLED=true and DEFAULT_CLUSTER_NAME are configured.
Note: This endpoint is unavailable during degraded mode (503).
Input parameters
| Parameter | In | Type | Default | Nullable | Description |
|---|---|---|---|---|---|
| apiKey | header | string | N/A | No | API key for REST API access (API_KEY or MASTER_KEY) |
Response 201 Created¶
application/json
{
"data": {
"cluster_name": "prod-east",
"expired_at": "2026-12-31T23:59:59Z",
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"inserted_at": "2025-06-09T08:00:00Z",
"key": "eyJzZXJ2ZXIiOiJodHRwczovL25ldG1ha2VyLmV4YW1wbGUuY29tIiwia2V5IjoiYWJjMTIzIn0=",
"last_used_at": null,
"name": "prod rollout",
"updated_at": "2025-06-09T08:00:00Z",
"uses_remaining": 5
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "Single enrollment key response",
"properties": {
"data": {
"$ref": "#/components/schemas/EnrollmentKeyResponse"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"data",
"meta"
],
"title": "EnrollmentKeySingleResponse",
"type": "object"
}
Response 403 Forbidden¶
application/json
{
"error": {
"code": "forbidden",
"message": "Insufficient permissions"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "403 Forbidden",
"example": {
"error": {
"code": "forbidden",
"message": "Insufficient permissions"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "forbidden",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Insufficient permissions",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "ForbiddenResponse",
"type": "object"
}
Response 404 Not Found¶
application/json
{
"error": {
"code": "not_found",
"message": "Resource not found"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "404 Not Found",
"example": {
"error": {
"code": "not_found",
"message": "Resource not found"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "not_found",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Resource not found",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "NotFoundResponse",
"type": "object"
}
Response 503 Service Unavailable¶
application/json
{
"error": {
"code": "service_unavailable",
"message": "Downstream dependency unreachable"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "503 Service Unavailable",
"example": {
"error": {
"code": "service_unavailable",
"message": "Downstream dependency unreachable"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "service_unavailable",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Downstream dependency unreachable",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "ServiceUnavailableResponse",
"type": "object"
}
POST /api/v1/clusters/default/enrollment_keys¶
Create an enrollment key for the default cluster
Description Convenience endpoint for the default cluster (configured via DEFAULT_CLUSTER_NAME env).
Note: This endpoint is unavailable during degraded mode (503).
Input parameters
| Parameter | In | Type | Default | Nullable | Description |
|---|---|---|---|---|---|
| apiKey | header | string | N/A | No | API key for REST API access (API_KEY or MASTER_KEY) |
Request body
application/json
This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.Schema of the request body
{
"description": "Parameters for creating a new enrollment key for a cluster. All fields are optional.",
"example": {
"expired_at": "2026-12-31T23:59:59Z",
"name": "prod rollout",
"uses_remaining": 5
},
"properties": {
"expired_at": {
"description": "Expiry datetime (ISO 8601). Omit or pass null for no expiry.",
"example": "2026-12-31T23:59:59Z",
"format": "date-time",
"nullable": true,
"type": "string"
},
"name": {
"description": "Optional human-readable label for this key. Display only — not used for lookup.",
"example": "prod rollout",
"nullable": true,
"type": "string"
},
"uses_remaining": {
"description": "Number of uses (must be >= 1). Pass null for unlimited. Omit to use the default of 1.",
"example": 5,
"minimum": 1,
"nullable": true,
"type": "integer"
}
},
"title": "EnrollmentKeyCreateRequest",
"type": "object"
}
Response 201 Created¶
application/json
{
"data": {
"cluster_name": "prod-east",
"expired_at": "2026-12-31T23:59:59Z",
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"inserted_at": "2025-06-09T08:00:00Z",
"key": "eyJzZXJ2ZXIiOiJodHRwczovL25ldG1ha2VyLmV4YW1wbGUuY29tIiwia2V5IjoiYWJjMTIzIn0=",
"last_used_at": null,
"name": "prod rollout",
"updated_at": "2025-06-09T08:00:00Z",
"uses_remaining": 5
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "Single enrollment key response",
"properties": {
"data": {
"$ref": "#/components/schemas/EnrollmentKeyResponse"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"data",
"meta"
],
"title": "EnrollmentKeySingleResponse",
"type": "object"
}
Response 403 Forbidden¶
application/json
{
"error": {
"code": "forbidden",
"message": "Insufficient permissions"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "403 Forbidden",
"example": {
"error": {
"code": "forbidden",
"message": "Insufficient permissions"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "forbidden",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Insufficient permissions",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "ForbiddenResponse",
"type": "object"
}
Response 404 Not Found¶
application/json
{
"error": {
"code": "not_found",
"message": "Resource not found"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "404 Not Found",
"example": {
"error": {
"code": "not_found",
"message": "Resource not found"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "not_found",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Resource not found",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "NotFoundResponse",
"type": "object"
}
Response 422 Unprocessable Entity¶
application/json
{
"error": {
"code": "validation_failed",
"details": {
"name": [
"can't be blank"
],
"timeout": [
"must be greater than 0"
]
},
"message": "Validation failed"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "422 Validation Failed",
"example": {
"error": {
"code": "validation_failed",
"details": {
"name": [
"can't be blank"
],
"timeout": [
"must be greater than 0"
]
},
"message": "Validation failed"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "validation_failed",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Validation failed",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "ChangesetErrorResponse",
"type": "object"
}
Response 503 Service Unavailable¶
application/json
{
"error": {
"code": "service_unavailable",
"message": "Downstream dependency unreachable"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "503 Service Unavailable",
"example": {
"error": {
"code": "service_unavailable",
"message": "Downstream dependency unreachable"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "service_unavailable",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Downstream dependency unreachable",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "ServiceUnavailableResponse",
"type": "object"
}
POST /api/v1/clusters/{cluster_name}/enrollment_keys¶
Create an enrollment key for a cluster
Description
Create a new enrollment key for an edge cluster. The returned key blob must be set as the ENROLLMENT_KEY
environment variable on the agent to allow it to join the cluster's VPN network.
Keys can be limited by use count (uses_remaining) or by expiry time (expired_at). Omit both for a single-use key with no expiry.
Note: This endpoint is unavailable during degraded mode (503).
Input parameters
| Parameter | In | Type | Default | Nullable | Description |
|---|---|---|---|---|---|
| apiKey | header | string | N/A | No | API key for REST API access (API_KEY or MASTER_KEY) |
| cluster_name | path | string | No | Cluster name |
Request body
application/json
This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.Schema of the request body
{
"description": "Parameters for creating a new enrollment key for a cluster. All fields are optional.",
"example": {
"expired_at": "2026-12-31T23:59:59Z",
"name": "prod rollout",
"uses_remaining": 5
},
"properties": {
"expired_at": {
"description": "Expiry datetime (ISO 8601). Omit or pass null for no expiry.",
"example": "2026-12-31T23:59:59Z",
"format": "date-time",
"nullable": true,
"type": "string"
},
"name": {
"description": "Optional human-readable label for this key. Display only — not used for lookup.",
"example": "prod rollout",
"nullable": true,
"type": "string"
},
"uses_remaining": {
"description": "Number of uses (must be >= 1). Pass null for unlimited. Omit to use the default of 1.",
"example": 5,
"minimum": 1,
"nullable": true,
"type": "integer"
}
},
"title": "EnrollmentKeyCreateRequest",
"type": "object"
}
Response 201 Created¶
application/json
{
"data": {
"cluster_name": "prod-east",
"expired_at": "2026-12-31T23:59:59Z",
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"inserted_at": "2025-06-09T08:00:00Z",
"key": "eyJzZXJ2ZXIiOiJodHRwczovL25ldG1ha2VyLmV4YW1wbGUuY29tIiwia2V5IjoiYWJjMTIzIn0=",
"last_used_at": null,
"name": "prod rollout",
"updated_at": "2025-06-09T08:00:00Z",
"uses_remaining": 5
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "Single enrollment key response",
"properties": {
"data": {
"$ref": "#/components/schemas/EnrollmentKeyResponse"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"data",
"meta"
],
"title": "EnrollmentKeySingleResponse",
"type": "object"
}
Response 404 Not Found¶
application/json
{
"error": {
"code": "not_found",
"message": "Resource not found"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "404 Not Found",
"example": {
"error": {
"code": "not_found",
"message": "Resource not found"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "not_found",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Resource not found",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "NotFoundResponse",
"type": "object"
}
Response 422 Unprocessable Entity¶
application/json
{
"error": {
"code": "validation_failed",
"details": {
"name": [
"can't be blank"
],
"timeout": [
"must be greater than 0"
]
},
"message": "Validation failed"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "422 Validation Failed",
"example": {
"error": {
"code": "validation_failed",
"details": {
"name": [
"can't be blank"
],
"timeout": [
"must be greater than 0"
]
},
"message": "Validation failed"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "validation_failed",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Validation failed",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "ChangesetErrorResponse",
"type": "object"
}
Response 503 Service Unavailable¶
application/json
{
"error": {
"code": "service_unavailable",
"message": "Downstream dependency unreachable"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "503 Service Unavailable",
"example": {
"error": {
"code": "service_unavailable",
"message": "Downstream dependency unreachable"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "service_unavailable",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Downstream dependency unreachable",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "ServiceUnavailableResponse",
"type": "object"
}
GET /api/v1/enrollment_keys¶
List enrollment keys
Description Returns a paginated list of enrollment keys with filtering and sorting.
Input parameters
| Parameter | In | Type | Default | Nullable | Description |
|---|---|---|---|---|---|
| apiKey | header | string | N/A | No | API key for REST API access (API_KEY or MASTER_KEY) |
| cluster_name | query | string | No | Filter by cluster name (exact match or wildcard: prod, east, etc.) | |
| expired_at__gte | query | No | Filter records where expired_at is on or after this datetime (ISO 8601 datetime; date-only is treated as start of day UTC) | ||
| expired_at__lte | query | No | Filter records where expired_at is on or before this datetime (ISO 8601 datetime; date-only is treated as end of day UTC) | ||
| has_expiry | query | boolean | No | Filter by whether the key has an expiry set: true returns keys with expired_at present, false returns keys with no expiry (unlimited lifetime) | |
| has_name | query | boolean | No | Filter by whether the key has a name set: true returns keys with a human-readable label, false returns unlabeled keys (e.g. those issued by the public/default-cluster endpoint) | |
| inserted_at__gte | query | No | Filter records where inserted_at is on or after this datetime (ISO 8601 datetime; date-only is treated as start of day UTC) | ||
| inserted_at__lte | query | No | Filter records where inserted_at is on or before this datetime (ISO 8601 datetime; date-only is treated as end of day UTC) | ||
| is_expired | query | boolean | No | Filter by whether the key is expired: true returns keys where expired_at is in the past, false returns active keys (including those with no expiry) | |
| is_never_used | query | boolean | No | Filter by whether the key has never been used: true returns keys where last_used_at is null, false returns keys that have been used at least once | |
| is_spent | query | boolean | No | Filter by whether the key is exhausted: true returns keys with uses_remaining == 0, false returns keys with uses remaining | |
| is_unlimited | query | boolean | No | Filter by whether the key has unlimited uses: true returns unlimited keys (uses_remaining is null), false returns keys with a finite use count | |
| key | query | string | No | Filter by exact key value | |
| last_used_at__gte | query | No | Filter records where last_used_at is on or after this datetime (ISO 8601 datetime; date-only is treated as start of day UTC) | ||
| last_used_at__lte | query | No | Filter records where last_used_at is on or before this datetime (ISO 8601 datetime; date-only is treated as end of day UTC) | ||
| name | query | string | No | Filter by enrollment key name (case-insensitive substring or wildcard: prod, rollout, etc.) | |
| order_by | query | string | No | Comma-separated list of fields to sort by | |
| order_directions | query | string | No | Comma-separated list of sort directions (asc/desc) corresponding to order_by fields | |
| page | query | integer | 1 | No | Page number (1-indexed) |
| page_size | query | integer | 20 | No | Items per page |
| updated_at__gte | query | No | Filter records where updated_at is on or after this datetime (ISO 8601 datetime; date-only is treated as start of day UTC) | ||
| updated_at__lte | query | No | Filter records where updated_at is on or before this datetime (ISO 8601 datetime; date-only is treated as end of day UTC) | ||
| uses_remaining | query | integer | No | Filter by exact uses_remaining (positive integer; use is_unlimited=true to find unlimited keys) | |
| uses_remaining__gte | query | integer | No | Filter by minimum uses_remaining | |
| uses_remaining__lte | query | integer | No | Filter by maximum uses_remaining |
Response 200 OK¶
application/json
{
"data": [
{
"cluster_name": "prod-east",
"expired_at": "2026-12-31T23:59:59Z",
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"inserted_at": "2025-06-09T08:00:00Z",
"key": "eyJzZXJ2ZXIiOiJodHRwczovL25ldG1ha2VyLmV4YW1wbGUuY29tIiwia2V5IjoiYWJjMTIzIn0=",
"last_used_at": null,
"name": "prod rollout",
"updated_at": "2025-06-09T08:00:00Z",
"uses_remaining": 5
}
],
"meta": {
"pagination": {
"has_next": true,
"has_prev": false,
"next_page": 2,
"page": 1,
"page_size": 20,
"prev_page": 0,
"total_count": 150,
"total_pages": 8
},
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "Paginated list of enrollment keys with filtering and sorting metadata",
"properties": {
"data": {
"items": {
"$ref": "#/components/schemas/EnrollmentKeyResponse"
},
"type": "array"
},
"meta": {
"$ref": "#/components/schemas/PaginatedMeta"
}
},
"required": [
"data",
"meta"
],
"title": "EnrollmentKeyPaginatedResponse",
"type": "object"
}
Response 400 Bad Request¶
application/json
{
"error": {
"code": "bad_request",
"details": {
"ipv4_range": [
"Invalid format."
],
"name": [
"Invalid format. Expected ~r/^[a-z0-9]/"
]
},
"message": "Invalid request parameters"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "400 Bad Request",
"example": {
"error": {
"code": "bad_request",
"details": {
"ipv4_range": [
"Invalid format."
],
"name": [
"Invalid format. Expected ~r/^[a-z0-9]/"
]
},
"message": "Invalid request parameters"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "bad_request",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Invalid request parameters",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "BadRequestResponse",
"type": "object"
}
GET /api/v1/enrollment_keys/{id}¶
Get an enrollment key
Description Returns details for a specific enrollment key by ID
Input parameters
| Parameter | In | Type | Default | Nullable | Description |
|---|---|---|---|---|---|
| apiKey | header | string | N/A | No | API key for REST API access (API_KEY or MASTER_KEY) |
| id | path | string | No | Enrollment key ID |
Response 200 OK¶
application/json
{
"data": {
"cluster_name": "prod-east",
"expired_at": "2026-12-31T23:59:59Z",
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"inserted_at": "2025-06-09T08:00:00Z",
"key": "eyJzZXJ2ZXIiOiJodHRwczovL25ldG1ha2VyLmV4YW1wbGUuY29tIiwia2V5IjoiYWJjMTIzIn0=",
"last_used_at": null,
"name": "prod rollout",
"updated_at": "2025-06-09T08:00:00Z",
"uses_remaining": 5
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "Single enrollment key response",
"properties": {
"data": {
"$ref": "#/components/schemas/EnrollmentKeyResponse"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"data",
"meta"
],
"title": "EnrollmentKeySingleResponse",
"type": "object"
}
Response 400 Bad Request¶
application/json
{
"error": {
"code": "bad_request",
"details": {
"ipv4_range": [
"Invalid format."
],
"name": [
"Invalid format. Expected ~r/^[a-z0-9]/"
]
},
"message": "Invalid request parameters"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "400 Bad Request",
"example": {
"error": {
"code": "bad_request",
"details": {
"ipv4_range": [
"Invalid format."
],
"name": [
"Invalid format. Expected ~r/^[a-z0-9]/"
]
},
"message": "Invalid request parameters"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "bad_request",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Invalid request parameters",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "BadRequestResponse",
"type": "object"
}
Response 404 Not Found¶
application/json
{
"error": {
"code": "not_found",
"message": "Resource not found"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "404 Not Found",
"example": {
"error": {
"code": "not_found",
"message": "Resource not found"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "not_found",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Resource not found",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "NotFoundResponse",
"type": "object"
}
PATCH /api/v1/enrollment_keys/{id}¶
Update an enrollment key
Description Update expired_at or uses_remaining. Pass null to unset a nullable field.
Note: This endpoint is unavailable during degraded mode (503).
Input parameters
| Parameter | In | Type | Default | Nullable | Description |
|---|---|---|---|---|---|
| apiKey | header | string | N/A | No | API key for REST API access (API_KEY or MASTER_KEY) |
| id | path | string | No | Enrollment key ID |
Request body
application/json
This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.Schema of the request body
{
"description": "Parameters for updating an enrollment key. Only fields that are present in the request body are updated — omitting a field leaves it unchanged.\n\n- `name`: pass a string to set a label, or `null` to clear it.\n- `uses_remaining`: pass a positive integer to set a limit, or `null` to make the key unlimited.\n- `expired_at`: pass a datetime to set expiry, or `null` to remove expiry.\n",
"example": {
"expired_at": "2026-12-31T23:59:59Z",
"name": "prod rollout",
"uses_remaining": null
},
"properties": {
"expired_at": {
"description": "Expiry datetime (ISO 8601), or null to remove expiry. Omit to leave unchanged.",
"example": "2026-12-31T23:59:59Z",
"format": "date-time",
"nullable": true,
"type": "string"
},
"name": {
"description": "Human-readable label for this key, or null to clear. Omit to leave unchanged.",
"example": "prod rollout",
"nullable": true,
"type": "string"
},
"uses_remaining": {
"description": "Positive integer to set a use limit, or null to make the key unlimited. Omit to leave unchanged.",
"example": 10,
"minimum": 1,
"nullable": true,
"type": "integer"
}
},
"title": "EnrollmentKeyUpdateRequest",
"type": "object"
}
Response 200 OK¶
application/json
{
"data": {
"cluster_name": "prod-east",
"expired_at": "2026-12-31T23:59:59Z",
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"inserted_at": "2025-06-09T08:00:00Z",
"key": "eyJzZXJ2ZXIiOiJodHRwczovL25ldG1ha2VyLmV4YW1wbGUuY29tIiwia2V5IjoiYWJjMTIzIn0=",
"last_used_at": null,
"name": "prod rollout",
"updated_at": "2025-06-09T08:00:00Z",
"uses_remaining": 5
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "Single enrollment key response",
"properties": {
"data": {
"$ref": "#/components/schemas/EnrollmentKeyResponse"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"data",
"meta"
],
"title": "EnrollmentKeySingleResponse",
"type": "object"
}
Response 404 Not Found¶
application/json
{
"error": {
"code": "not_found",
"message": "Resource not found"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "404 Not Found",
"example": {
"error": {
"code": "not_found",
"message": "Resource not found"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "not_found",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Resource not found",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "NotFoundResponse",
"type": "object"
}
Response 422 Unprocessable Entity¶
application/json
{
"error": {
"code": "validation_failed",
"details": {
"name": [
"can't be blank"
],
"timeout": [
"must be greater than 0"
]
},
"message": "Validation failed"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "422 Validation Failed",
"example": {
"error": {
"code": "validation_failed",
"details": {
"name": [
"can't be blank"
],
"timeout": [
"must be greater than 0"
]
},
"message": "Validation failed"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "validation_failed",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Validation failed",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "ChangesetErrorResponse",
"type": "object"
}
Response 503 Service Unavailable¶
application/json
{
"error": {
"code": "service_unavailable",
"message": "Downstream dependency unreachable"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "503 Service Unavailable",
"example": {
"error": {
"code": "service_unavailable",
"message": "Downstream dependency unreachable"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "service_unavailable",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Downstream dependency unreachable",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "ServiceUnavailableResponse",
"type": "object"
}
DELETE /api/v1/enrollment_keys/{id}¶
Delete an enrollment key
Description Permanently deletes an enrollment key. Note: This endpoint is unavailable during degraded mode (503).
Input parameters
| Parameter | In | Type | Default | Nullable | Description |
|---|---|---|---|---|---|
| apiKey | header | string | N/A | No | API key for REST API access (API_KEY or MASTER_KEY) |
| id | path | string | No | Enrollment key ID |
Response 204 No Content¶
Schema of the response body
Response 400 Bad Request¶
application/json
{
"error": {
"code": "bad_request",
"details": {
"ipv4_range": [
"Invalid format."
],
"name": [
"Invalid format. Expected ~r/^[a-z0-9]/"
]
},
"message": "Invalid request parameters"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "400 Bad Request",
"example": {
"error": {
"code": "bad_request",
"details": {
"ipv4_range": [
"Invalid format."
],
"name": [
"Invalid format. Expected ~r/^[a-z0-9]/"
]
},
"message": "Invalid request parameters"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "bad_request",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Invalid request parameters",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "BadRequestResponse",
"type": "object"
}
Response 404 Not Found¶
application/json
{
"error": {
"code": "not_found",
"message": "Resource not found"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "404 Not Found",
"example": {
"error": {
"code": "not_found",
"message": "Resource not found"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "not_found",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Resource not found",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "NotFoundResponse",
"type": "object"
}
Response 503 Service Unavailable¶
application/json
{
"error": {
"code": "service_unavailable",
"message": "Downstream dependency unreachable"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "503 Service Unavailable",
"example": {
"error": {
"code": "service_unavailable",
"message": "Downstream dependency unreachable"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "service_unavailable",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Downstream dependency unreachable",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "ServiceUnavailableResponse",
"type": "object"
}
Nodes.Node¶
GET /api/v1/nodes¶
List all nodes
Description Returns a paginated list of all registered edge nodes with filtering and sorting
Input parameters
| Parameter | In | Type | Default | Nullable | Description |
|---|---|---|---|---|---|
| apiKey | header | string | N/A | No | API key for REST API access (API_KEY or MASTER_KEY) |
| cluster_name | query | string | No | Filter by cluster name (exact match or wildcard: prod, east, etc.) | |
| id_type | query | string | No | Filter by node ID type | |
| inserted_at__gte | query | No | Filter records where inserted_at is on or after this datetime (ISO 8601 datetime; date-only is treated as start of day UTC) | ||
| inserted_at__lte | query | No | Filter records where inserted_at is on or before this datetime (ISO 8601 datetime; date-only is treated as end of day UTC) | ||
| last_seen_at__gte | query | No | Filter records where last_seen_at is on or after this datetime (ISO 8601 datetime; date-only is treated as start of day UTC) | ||
| last_seen_at__lte | query | No | Filter records where last_seen_at is on or before this datetime (ISO 8601 datetime; date-only is treated as end of day UTC) | ||
| order_by | query | string | No | Comma-separated list of fields to sort by | |
| order_directions | query | string | No | Comma-separated list of sort directions (asc/desc) corresponding to order_by fields | |
| page | query | integer | 1 | No | Page number (1-indexed) |
| page_size | query | integer | 20 | No | Items per page |
| self_update_enabled | query | boolean | No | Filter by self-update enabled status | |
| status | query | string | No | Filter by node status | |
| updated_at__gte | query | No | Filter records where updated_at is on or after this datetime (ISO 8601 datetime; date-only is treated as start of day UTC) | ||
| updated_at__lte | query | No | Filter records where updated_at is on or before this datetime (ISO 8601 datetime; date-only is treated as end of day UTC) | ||
| version | query | string | No | Filter by agent version (exact match or wildcard: 1.0.0, 1.*, etc.) |
Response 200 OK¶
application/json
{
"data": [
{
"api_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"cluster_name": "prod-east",
"host_metrics_port": 49100,
"http_port": 44000,
"http_proxy_port": 44880,
"id": "01234567-89ab-cdef-0123-456789abcdef",
"id_type": "persistent",
"inserted_at": "2025-06-09T08:00:00Z",
"last_seen_at": "2025-06-09T08:20:00Z",
"mdns_hostname": "node-01234567-89ab-cdef-0123-456789abcdef.local",
"netmaker_host_id": "def67890-5678-5678-5678-567890abcdef",
"node_name": "node-01234567-89ab-cdef-0123-456789abcdef",
"proxy_password": "securepassword123",
"self_update_enabled": false,
"socks5_proxy_port": 44180,
"ssh_port": 42222,
"status": "healthy",
"updated_at": "2025-06-09T08:20:00Z",
"version": "0.1.0",
"vpn_hostname": "node-01234567-89ab-cdef-0123-456789abcdef.cluster-prod-east.nm.internal",
"wireguard_metrics_port": 49586
}
],
"meta": {
"pagination": {
"has_next": true,
"has_prev": false,
"next_page": 2,
"page": 1,
"page_size": 20,
"prev_page": 0,
"total_count": 150,
"total_pages": 8
},
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "Paginated list of nodes with filtering and sorting metadata",
"properties": {
"data": {
"items": {
"$ref": "#/components/schemas/NodeResponse"
},
"type": "array"
},
"meta": {
"$ref": "#/components/schemas/PaginatedMeta"
}
},
"required": [
"data",
"meta"
],
"title": "NodePaginatedResponse",
"type": "object"
}
Response 400 Bad Request¶
application/json
{
"error": {
"code": "bad_request",
"details": {
"ipv4_range": [
"Invalid format."
],
"name": [
"Invalid format. Expected ~r/^[a-z0-9]/"
]
},
"message": "Invalid request parameters"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "400 Bad Request",
"example": {
"error": {
"code": "bad_request",
"details": {
"ipv4_range": [
"Invalid format."
],
"name": [
"Invalid format. Expected ~r/^[a-z0-9]/"
]
},
"message": "Invalid request parameters"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "bad_request",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Invalid request parameters",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "BadRequestResponse",
"type": "object"
}
GET /api/v1/nodes/{id}¶
Get a specific node
Description Returns details for a specific node by ID
Input parameters
| Parameter | In | Type | Default | Nullable | Description |
|---|---|---|---|---|---|
| apiKey | header | string | N/A | No | API key for REST API access (API_KEY or MASTER_KEY) |
| id | path | string | No | Node ID |
Response 200 OK¶
application/json
{
"data": {
"api_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"cluster_name": "prod-east",
"host_metrics_port": 49100,
"http_port": 44000,
"http_proxy_port": 44880,
"id": "01234567-89ab-cdef-0123-456789abcdef",
"id_type": "persistent",
"inserted_at": "2025-06-09T08:00:00Z",
"last_seen_at": "2025-06-09T08:20:00Z",
"mdns_hostname": "node-01234567-89ab-cdef-0123-456789abcdef.local",
"netmaker_host_id": "def67890-5678-5678-5678-567890abcdef",
"node_name": "node-01234567-89ab-cdef-0123-456789abcdef",
"proxy_password": "securepassword123",
"self_update_enabled": false,
"socks5_proxy_port": 44180,
"ssh_port": 42222,
"status": "healthy",
"updated_at": "2025-06-09T08:20:00Z",
"version": "0.1.0",
"vpn_hostname": "node-01234567-89ab-cdef-0123-456789abcdef.cluster-prod-east.nm.internal",
"wireguard_metrics_port": 49586
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "Single node response",
"properties": {
"data": {
"$ref": "#/components/schemas/NodeResponse"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"data",
"meta"
],
"title": "NodeSingleResponse",
"type": "object"
}
Response 400 Bad Request¶
application/json
{
"error": {
"code": "bad_request",
"details": {
"ipv4_range": [
"Invalid format."
],
"name": [
"Invalid format. Expected ~r/^[a-z0-9]/"
]
},
"message": "Invalid request parameters"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "400 Bad Request",
"example": {
"error": {
"code": "bad_request",
"details": {
"ipv4_range": [
"Invalid format."
],
"name": [
"Invalid format. Expected ~r/^[a-z0-9]/"
]
},
"message": "Invalid request parameters"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "bad_request",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Invalid request parameters",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "BadRequestResponse",
"type": "object"
}
Response 404 Not Found¶
application/json
{
"error": {
"code": "not_found",
"message": "Resource not found"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "404 Not Found",
"example": {
"error": {
"code": "not_found",
"message": "Resource not found"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "not_found",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Resource not found",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "NotFoundResponse",
"type": "object"
}
DELETE /api/v1/nodes/{id}¶
Delete a node
Description
Delete a node. Removes the Netmaker host first, then the DB row. Cascade: ssh_usernames (and their ssh_public_keys) and aliases are deleted; command_executions are kept with node_id set to NULL for history.
Note: This endpoint is unavailable during degraded mode (503).
Input parameters
| Parameter | In | Type | Default | Nullable | Description |
|---|---|---|---|---|---|
| apiKey | header | string | N/A | No | API key for REST API access (API_KEY or MASTER_KEY) |
| id | path | string | No | Node ID |
Response 204 No Content¶
Schema of the response body
Response 400 Bad Request¶
application/json
{
"error": {
"code": "bad_request",
"details": {
"ipv4_range": [
"Invalid format."
],
"name": [
"Invalid format. Expected ~r/^[a-z0-9]/"
]
},
"message": "Invalid request parameters"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "400 Bad Request",
"example": {
"error": {
"code": "bad_request",
"details": {
"ipv4_range": [
"Invalid format."
],
"name": [
"Invalid format. Expected ~r/^[a-z0-9]/"
]
},
"message": "Invalid request parameters"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "bad_request",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Invalid request parameters",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "BadRequestResponse",
"type": "object"
}
Response 404 Not Found¶
application/json
{
"error": {
"code": "not_found",
"message": "Resource not found"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "404 Not Found",
"example": {
"error": {
"code": "not_found",
"message": "Resource not found"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "not_found",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Resource not found",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "NotFoundResponse",
"type": "object"
}
Response 503 Service Unavailable¶
application/json
{
"error": {
"code": "service_unavailable",
"message": "Downstream dependency unreachable"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "503 Service Unavailable",
"example": {
"error": {
"code": "service_unavailable",
"message": "Downstream dependency unreachable"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "service_unavailable",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Downstream dependency unreachable",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "ServiceUnavailableResponse",
"type": "object"
}
PATCH /api/v1/nodes/{id}/change_cluster¶
Change a node's cluster
Description Move a node to a different cluster. Performs cluster migration via Netmaker (best-effort, reconciliation worker handles failures).
Note: This endpoint is unavailable during degraded mode (503).
Input parameters
| Parameter | In | Type | Default | Nullable | Description |
|---|---|---|---|---|---|
| apiKey | header | string | N/A | No | API key for REST API access (API_KEY or MASTER_KEY) |
| id | path | string | No | Node ID |
Request body
application/json
This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.Schema of the request body
{
"description": "Request to move a node to a different cluster. Performs cluster migration via Netmaker (best-effort, reconciliation worker handles failures).",
"example": {
"cluster_name": "prod-west"
},
"properties": {
"cluster_name": {
"description": "Name of the target cluster to move this node to.",
"example": "prod-west",
"maxLength": 24,
"pattern": "^[a-z0-9]([a-z0-9-]*[a-z0-9])?$",
"type": "string"
}
},
"required": [
"cluster_name"
],
"title": "ChangeClusterRequest",
"type": "object"
}
Response 200 OK¶
application/json
{
"data": {
"api_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"cluster_name": "prod-east",
"host_metrics_port": 49100,
"http_port": 44000,
"http_proxy_port": 44880,
"id": "01234567-89ab-cdef-0123-456789abcdef",
"id_type": "persistent",
"inserted_at": "2025-06-09T08:00:00Z",
"last_seen_at": "2025-06-09T08:20:00Z",
"mdns_hostname": "node-01234567-89ab-cdef-0123-456789abcdef.local",
"netmaker_host_id": "def67890-5678-5678-5678-567890abcdef",
"node_name": "node-01234567-89ab-cdef-0123-456789abcdef",
"proxy_password": "securepassword123",
"self_update_enabled": false,
"socks5_proxy_port": 44180,
"ssh_port": 42222,
"status": "healthy",
"updated_at": "2025-06-09T08:20:00Z",
"version": "0.1.0",
"vpn_hostname": "node-01234567-89ab-cdef-0123-456789abcdef.cluster-prod-east.nm.internal",
"wireguard_metrics_port": 49586
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "Single node response",
"properties": {
"data": {
"$ref": "#/components/schemas/NodeResponse"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"data",
"meta"
],
"title": "NodeSingleResponse",
"type": "object"
}
Response 404 Not Found¶
application/json
{
"error": {
"code": "not_found",
"message": "Resource not found"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "404 Not Found",
"example": {
"error": {
"code": "not_found",
"message": "Resource not found"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "not_found",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Resource not found",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "NotFoundResponse",
"type": "object"
}
Response 409 Conflict¶
application/json
{
"error": {
"code": "conflict",
"message": "Resource already exists"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "409 Conflict",
"example": {
"error": {
"code": "conflict",
"message": "Resource already exists"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "conflict",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Resource already exists",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "ConflictResponse",
"type": "object"
}
Response 422 Unprocessable Entity¶
application/json
{
"error": {
"code": "validation_failed",
"details": {
"name": [
"can't be blank"
],
"timeout": [
"must be greater than 0"
]
},
"message": "Validation failed"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "422 Validation Failed",
"example": {
"error": {
"code": "validation_failed",
"details": {
"name": [
"can't be blank"
],
"timeout": [
"must be greater than 0"
]
},
"message": "Validation failed"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "validation_failed",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Validation failed",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "ChangesetErrorResponse",
"type": "object"
}
Response 503 Service Unavailable¶
application/json
{
"error": {
"code": "service_unavailable",
"message": "Downstream dependency unreachable"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "503 Service Unavailable",
"example": {
"error": {
"code": "service_unavailable",
"message": "Downstream dependency unreachable"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "service_unavailable",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Downstream dependency unreachable",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "ServiceUnavailableResponse",
"type": "object"
}
Nodes.Alias¶
POST /api/v1/nodes/{node_id}/aliases¶
Create a new alias for a node
Description Creates a new alias and corresponding DNS entry for the specified node
Input parameters
| Parameter | In | Type | Default | Nullable | Description |
|---|---|---|---|---|---|
| apiKey | header | string | N/A | No | API key for REST API access (API_KEY or MASTER_KEY) |
| node_id | path | string | No | Node ID |
Request body
application/json
This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.Schema of the request body
{
"description": "Parameters for creating a new DNS alias for a node",
"example": {
"name": "web-server"
},
"properties": {
"name": {
"description": "Alias name (lowercase alphanumeric with hyphens)",
"example": "web-server",
"maxLength": 63,
"minLength": 1,
"pattern": "^[a-z0-9]([a-z0-9-]*[a-z0-9])?$",
"type": "string"
}
},
"required": [
"name"
],
"title": "CreateAliasRequest",
"type": "object"
}
Response 201 Created¶
application/json
{
"data": {
"cluster_name": "prod",
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"inserted_at": "2024-01-15T10:30:00Z",
"name": "web-server",
"node_id": "01234567-89ab-cdef-0123-456789abcdef",
"updated_at": "2024-01-15T10:30:00Z",
"vpn_hostname": "node-web-server.cluster-prod.nm.internal"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "Single alias response",
"properties": {
"data": {
"$ref": "#/components/schemas/AliasResponse"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"data",
"meta"
],
"title": "AliasSingleResponse",
"type": "object"
}
Response 404 Not Found¶
application/json
{
"error": {
"code": "not_found",
"message": "Resource not found"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "404 Not Found",
"example": {
"error": {
"code": "not_found",
"message": "Resource not found"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "not_found",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Resource not found",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "NotFoundResponse",
"type": "object"
}
Response 409 Conflict¶
application/json
{
"error": {
"code": "conflict",
"message": "Resource already exists"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "409 Conflict",
"example": {
"error": {
"code": "conflict",
"message": "Resource already exists"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "conflict",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Resource already exists",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "ConflictResponse",
"type": "object"
}
Response 422 Unprocessable Entity¶
application/json
{
"error": {
"code": "validation_failed",
"details": {
"name": [
"can't be blank"
],
"timeout": [
"must be greater than 0"
]
},
"message": "Validation failed"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "422 Validation Failed",
"example": {
"error": {
"code": "validation_failed",
"details": {
"name": [
"can't be blank"
],
"timeout": [
"must be greater than 0"
]
},
"message": "Validation failed"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "validation_failed",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Validation failed",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "ChangesetErrorResponse",
"type": "object"
}
Response 503 Service Unavailable¶
application/json
{
"error": {
"code": "service_unavailable",
"message": "Downstream dependency unreachable"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "503 Service Unavailable",
"example": {
"error": {
"code": "service_unavailable",
"message": "Downstream dependency unreachable"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "service_unavailable",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Downstream dependency unreachable",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "ServiceUnavailableResponse",
"type": "object"
}
GET /api/v1/aliases¶
List all aliases
Description Returns a paginated list of node aliases with filtering and sorting
Input parameters
| Parameter | In | Type | Default | Nullable | Description |
|---|---|---|---|---|---|
| apiKey | header | string | N/A | No | API key for REST API access (API_KEY or MASTER_KEY) |
| cluster_name | query | string | No | Filter by cluster name (exact match or wildcard: prod, east, etc.) | |
| inserted_at__gte | query | No | Filter records where inserted_at is on or after this datetime (ISO 8601 datetime; date-only is treated as start of day UTC) | ||
| inserted_at__lte | query | No | Filter records where inserted_at is on or before this datetime (ISO 8601 datetime; date-only is treated as end of day UTC) | ||
| name | query | string | No | Filter by alias name (exact match or wildcard: prod, east, etc.) | |
| node_id | query | string | No | Filter by node ID (exact match UUID) | |
| order_by | query | string | No | Comma-separated list of fields to sort by | |
| order_directions | query | string | No | Comma-separated list of sort directions (asc/desc) corresponding to order_by fields | |
| page | query | integer | 1 | No | Page number (1-indexed) |
| page_size | query | integer | 20 | No | Items per page |
| updated_at__gte | query | No | Filter records where updated_at is on or after this datetime (ISO 8601 datetime; date-only is treated as start of day UTC) | ||
| updated_at__lte | query | No | Filter records where updated_at is on or before this datetime (ISO 8601 datetime; date-only is treated as end of day UTC) |
Response 200 OK¶
application/json
{
"data": [
{
"cluster_name": "prod",
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"inserted_at": "2024-01-15T10:30:00Z",
"name": "web-server",
"node_id": "01234567-89ab-cdef-0123-456789abcdef",
"updated_at": "2024-01-15T10:30:00Z",
"vpn_hostname": "node-web-server.cluster-prod.nm.internal"
}
],
"meta": {
"pagination": {
"has_next": true,
"has_prev": false,
"next_page": 2,
"page": 1,
"page_size": 20,
"prev_page": 0,
"total_count": 150,
"total_pages": 8
},
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "Paginated list of aliases with filtering and sorting metadata",
"properties": {
"data": {
"items": {
"$ref": "#/components/schemas/AliasResponse"
},
"type": "array"
},
"meta": {
"$ref": "#/components/schemas/PaginatedMeta"
}
},
"required": [
"data",
"meta"
],
"title": "AliasPaginatedResponse",
"type": "object"
}
Response 400 Bad Request¶
application/json
{
"error": {
"code": "bad_request",
"details": {
"ipv4_range": [
"Invalid format."
],
"name": [
"Invalid format. Expected ~r/^[a-z0-9]/"
]
},
"message": "Invalid request parameters"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "400 Bad Request",
"example": {
"error": {
"code": "bad_request",
"details": {
"ipv4_range": [
"Invalid format."
],
"name": [
"Invalid format. Expected ~r/^[a-z0-9]/"
]
},
"message": "Invalid request parameters"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "bad_request",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Invalid request parameters",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "BadRequestResponse",
"type": "object"
}
GET /api/v1/aliases/{id}¶
Get a specific alias
Description Returns details for a specific alias by ID
Input parameters
| Parameter | In | Type | Default | Nullable | Description |
|---|---|---|---|---|---|
| apiKey | header | string | N/A | No | API key for REST API access (API_KEY or MASTER_KEY) |
| id | path | string | No | Alias ID |
Response 200 OK¶
application/json
{
"data": {
"cluster_name": "prod",
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"inserted_at": "2024-01-15T10:30:00Z",
"name": "web-server",
"node_id": "01234567-89ab-cdef-0123-456789abcdef",
"updated_at": "2024-01-15T10:30:00Z",
"vpn_hostname": "node-web-server.cluster-prod.nm.internal"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "Single alias response",
"properties": {
"data": {
"$ref": "#/components/schemas/AliasResponse"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"data",
"meta"
],
"title": "AliasSingleResponse",
"type": "object"
}
Response 400 Bad Request¶
application/json
{
"error": {
"code": "bad_request",
"details": {
"ipv4_range": [
"Invalid format."
],
"name": [
"Invalid format. Expected ~r/^[a-z0-9]/"
]
},
"message": "Invalid request parameters"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "400 Bad Request",
"example": {
"error": {
"code": "bad_request",
"details": {
"ipv4_range": [
"Invalid format."
],
"name": [
"Invalid format. Expected ~r/^[a-z0-9]/"
]
},
"message": "Invalid request parameters"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "bad_request",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Invalid request parameters",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "BadRequestResponse",
"type": "object"
}
Response 404 Not Found¶
application/json
{
"error": {
"code": "not_found",
"message": "Resource not found"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "404 Not Found",
"example": {
"error": {
"code": "not_found",
"message": "Resource not found"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "not_found",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Resource not found",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "NotFoundResponse",
"type": "object"
}
DELETE /api/v1/aliases/{id}¶
Delete an alias
Description Deletes an alias and its corresponding DNS entry
Input parameters
| Parameter | In | Type | Default | Nullable | Description |
|---|---|---|---|---|---|
| apiKey | header | string | N/A | No | API key for REST API access (API_KEY or MASTER_KEY) |
| id | path | string | No | Alias ID |
Response 204 No Content¶
Schema of the response body
Response 400 Bad Request¶
application/json
{
"error": {
"code": "bad_request",
"details": {
"ipv4_range": [
"Invalid format."
],
"name": [
"Invalid format. Expected ~r/^[a-z0-9]/"
]
},
"message": "Invalid request parameters"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "400 Bad Request",
"example": {
"error": {
"code": "bad_request",
"details": {
"ipv4_range": [
"Invalid format."
],
"name": [
"Invalid format. Expected ~r/^[a-z0-9]/"
]
},
"message": "Invalid request parameters"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "bad_request",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Invalid request parameters",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "BadRequestResponse",
"type": "object"
}
Response 404 Not Found¶
application/json
{
"error": {
"code": "not_found",
"message": "Resource not found"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "404 Not Found",
"example": {
"error": {
"code": "not_found",
"message": "Resource not found"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "not_found",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Resource not found",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "NotFoundResponse",
"type": "object"
}
Response 503 Service Unavailable¶
application/json
{
"error": {
"code": "service_unavailable",
"message": "Downstream dependency unreachable"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "503 Service Unavailable",
"example": {
"error": {
"code": "service_unavailable",
"message": "Downstream dependency unreachable"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "service_unavailable",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Downstream dependency unreachable",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "ServiceUnavailableResponse",
"type": "object"
}
Nodes.Metrics¶
GET /api/v1/nodes/{node_id}/metrics¶
Get unified metrics for a node
Description Returns aggregated metrics from all available sources for a node: - Host metrics (Node Exporter): CPU, memory, disk, uptime - Agent metrics (agent PromEx): BEAM stats, commands, proxy, SSH, VPN, health check, Oban
Provides a complete view of node health and performance in a single request. Uses best-effort fetching - if one source fails, others are still returned.
Input parameters
| Parameter | In | Type | Default | Nullable | Description |
|---|---|---|---|---|---|
| apiKey | header | string | N/A | No | API key for REST API access (API_KEY or MASTER_KEY) |
| node_id | path | string | No | Node UUID |
Response 200 OK¶
application/json
{
"data": {
"agent": {
"application": {},
"available": true,
"commands": {},
"discovery": {},
"health_check": {},
"oban_queues": [
{}
],
"proxy": {},
"ssh": {},
"vpn": {}
},
"cluster_name": "string",
"host": {
"available": true,
"cpu": {},
"disk": {},
"memory": {},
"uptime": {}
},
"node_id": "de6cdeeb-184d-4cdd-a56d-52986bd14811",
"timestamp": "2022-04-13T15:42:05.901Z"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "Complete metrics from all sources: host (Node Exporter) and agent (PromEx).\nProvides a unified view of node health and performance.\nUses best-effort fetching - if one source fails, it's marked as unavailable.\n",
"properties": {
"data": {
"properties": {
"agent": {
"description": "Agent application metrics from PromEx",
"properties": {
"application": {
"description": "BEAM VM stats",
"nullable": true,
"type": "object"
},
"available": {
"description": "Whether agent metrics were successfully fetched",
"type": "boolean"
},
"commands": {
"description": "Command execution metrics",
"nullable": true,
"type": "object"
},
"discovery": {
"description": "Admin discovery metrics",
"nullable": true,
"type": "object"
},
"health_check": {
"description": "Health check report metrics (HTTP fallback mode)",
"nullable": true,
"type": "object"
},
"oban_queues": {
"description": "Oban job queue states",
"items": {
"type": "object"
},
"nullable": true,
"type": "array"
},
"proxy": {
"description": "Proxy server metrics",
"nullable": true,
"type": "object"
},
"ssh": {
"description": "SSH server metrics",
"nullable": true,
"type": "object"
},
"vpn": {
"description": "VPN config pull metrics",
"nullable": true,
"type": "object"
}
},
"type": "object"
},
"cluster_name": {
"description": "Name of the cluster this node belongs to",
"type": "string"
},
"host": {
"description": "Host-level metrics from Node Exporter",
"properties": {
"available": {
"description": "Whether host metrics were successfully fetched",
"type": "boolean"
},
"cpu": {
"description": "CPU metrics",
"nullable": true,
"type": "object"
},
"disk": {
"description": "Disk metrics",
"nullable": true,
"type": "object"
},
"memory": {
"description": "Memory metrics",
"nullable": true,
"type": "object"
},
"uptime": {
"description": "Uptime information",
"nullable": true,
"type": "object"
}
},
"type": "object"
},
"node_id": {
"description": "Node unique identifier",
"format": "uuid",
"type": "string"
},
"timestamp": {
"description": "When the metrics were collected (ISO 8601 format)",
"format": "date-time",
"type": "string"
}
},
"required": [
"node_id",
"cluster_name",
"timestamp"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"data",
"meta"
],
"title": "UnifiedMetricsResponse",
"type": "object"
}
Response 404 Not Found¶
application/json
{
"error": {
"code": "not_found",
"message": "Resource not found"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "404 Not Found",
"example": {
"error": {
"code": "not_found",
"message": "Resource not found"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "not_found",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Resource not found",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "NotFoundResponse",
"type": "object"
}
Response 503 Service Unavailable¶
application/json
{
"error": {
"code": "service_unavailable",
"message": "Downstream dependency unreachable"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "503 Service Unavailable",
"example": {
"error": {
"code": "service_unavailable",
"message": "Downstream dependency unreachable"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "service_unavailable",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Downstream dependency unreachable",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "ServiceUnavailableResponse",
"type": "object"
}
GET /api/v1/nodes/{node_id}/metrics/host¶
Get host metrics for a node
Description Returns host-level system metrics from Node Exporter: - CPU: cores, load averages - Memory: usage, total/available/used in bytes and GB - Disk: usage, total/available/used for root filesystem - Uptime: seconds and human-readable format
Input parameters
| Parameter | In | Type | Default | Nullable | Description |
|---|---|---|---|---|---|
| apiKey | header | string | N/A | No | API key for REST API access (API_KEY or MASTER_KEY) |
| node_id | path | string | No | Node UUID |
Response 200 OK¶
application/json
{
"cluster_name": "production",
"cpu": {
"cores": 4,
"load_15m": 0.9,
"load_1m": 1.2,
"load_5m": 1.1,
"usage_percent": 25.5
},
"disk": {
"available_bytes": 58249036800,
"available_gb": 54.2,
"total_bytes": 107374182400,
"total_gb": 100.0,
"usage_percent": 45.8,
"used_bytes": 49125145600,
"used_gb": 45.8
},
"memory": {
"available_bytes": 2814377984,
"available_gb": 2.6,
"total_bytes": 8589934592,
"total_gb": 8.0,
"usage_percent": 67.3,
"used_bytes": 5775556608,
"used_gb": 5.4
},
"node_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2025-01-15T15:30:00Z",
"uptime": {
"human": "1d 1h 1m",
"seconds": 90061
}
}
Schema of the response body
{
"description": "Host-level system metrics from Node Exporter (CPU, memory, disk, uptime).\n",
"example": {
"cluster_name": "production",
"cpu": {
"cores": 4,
"load_15m": 0.9,
"load_1m": 1.2,
"load_5m": 1.1,
"usage_percent": 25.5
},
"disk": {
"available_bytes": 58249036800,
"available_gb": 54.2,
"total_bytes": 107374182400,
"total_gb": 100.0,
"usage_percent": 45.8,
"used_bytes": 49125145600,
"used_gb": 45.8
},
"memory": {
"available_bytes": 2814377984,
"available_gb": 2.6,
"total_bytes": 8589934592,
"total_gb": 8.0,
"usage_percent": 67.3,
"used_bytes": 5775556608,
"used_gb": 5.4
},
"node_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2025-01-15T15:30:00Z",
"uptime": {
"human": "1d 1h 1m",
"seconds": 90061
}
},
"properties": {
"cluster_name": {
"description": "Name of the cluster this node belongs to",
"type": "string"
},
"cpu": {
"description": "CPU metrics",
"properties": {
"cores": {
"description": "Number of CPU cores detected on the system",
"nullable": true,
"type": "integer"
},
"load_15m": {
"description": "System load average over the last 15 minutes",
"nullable": true,
"type": "number"
},
"load_1m": {
"description": "System load average over the last 1 minute",
"nullable": true,
"type": "number"
},
"load_5m": {
"description": "System load average over the last 5 minutes",
"nullable": true,
"type": "number"
}
},
"type": "object"
},
"disk": {
"description": "Disk metrics for root filesystem (/)",
"properties": {
"available_bytes": {
"description": "Available disk space in bytes",
"nullable": true,
"type": "integer"
},
"available_gb": {
"description": "Available disk space in gigabytes (GB)",
"nullable": true,
"type": "number"
},
"total_bytes": {
"description": "Total disk space in bytes",
"nullable": true,
"type": "integer"
},
"total_gb": {
"description": "Total disk space in gigabytes (GB)",
"nullable": true,
"type": "number"
},
"usage_percent": {
"description": "Disk usage percentage calculated as (total - available) / total * 100",
"nullable": true,
"type": "number"
},
"used_bytes": {
"description": "Used disk space in bytes (calculated as total - available)",
"nullable": true,
"type": "integer"
},
"used_gb": {
"description": "Used disk space in gigabytes (GB)",
"nullable": true,
"type": "number"
}
},
"type": "object"
},
"memory": {
"description": "Memory metrics",
"properties": {
"available_bytes": {
"description": "Available RAM in bytes (includes buffers/cache)",
"nullable": true,
"type": "integer"
},
"available_gb": {
"description": "Available RAM in gigabytes (GB)",
"nullable": true,
"type": "number"
},
"total_bytes": {
"description": "Total RAM in bytes",
"nullable": true,
"type": "integer"
},
"total_gb": {
"description": "Total RAM in gigabytes (GB)",
"nullable": true,
"type": "number"
},
"usage_percent": {
"description": "Memory usage percentage calculated as (total - available) / total * 100",
"nullable": true,
"type": "number"
},
"used_bytes": {
"description": "Used RAM in bytes (calculated as total - available)",
"nullable": true,
"type": "integer"
},
"used_gb": {
"description": "Used RAM in gigabytes (GB)",
"nullable": true,
"type": "number"
}
},
"type": "object"
},
"node_id": {
"description": "Node unique identifier",
"format": "uuid",
"type": "string"
},
"timestamp": {
"description": "When the metrics were collected (ISO 8601 format)",
"format": "date-time",
"type": "string"
},
"uptime": {
"description": "System uptime information",
"properties": {
"human": {
"description": "Human-readable uptime format (e.g., '1d 2h 30m', '5h 15m', '30m')",
"nullable": true,
"type": "string"
},
"seconds": {
"description": "System uptime in seconds since last boot",
"nullable": true,
"type": "integer"
}
},
"type": "object"
}
},
"required": [
"node_id",
"cluster_name",
"timestamp"
],
"title": "HostMetricsResponse",
"type": "object"
}
Response 404 Not Found¶
application/json
{
"error": {
"code": "not_found",
"message": "Resource not found"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "404 Not Found",
"example": {
"error": {
"code": "not_found",
"message": "Resource not found"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "not_found",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Resource not found",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "NotFoundResponse",
"type": "object"
}
Response 503 Service Unavailable¶
application/json
{
"error": {
"code": "service_unavailable",
"message": "Downstream dependency unreachable"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "503 Service Unavailable",
"example": {
"error": {
"code": "service_unavailable",
"message": "Downstream dependency unreachable"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "service_unavailable",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Downstream dependency unreachable",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "ServiceUnavailableResponse",
"type": "object"
}
GET /api/v1/nodes/{node_id}/metrics/agent¶
Get agent metrics for a node
Description Returns application-level metrics from the edge_agent PromEx: - Application: uptime, BEAM stats (processes, memory breakdown) - Commands: sync/enqueue/complete/report statistics - Discovery: admin discovery scan metrics - Proxy: HTTP and SOCKS5 connection and blocked-request statistics - SSH: authentication attempts and connection count - VPN: config pull count (daily backstop for DNS recovery) - Health Check: fallback health report count (only non-zero when VPN is down) - Oban: job queue states (available, executing, completed, etc.)
Input parameters
| Parameter | In | Type | Default | Nullable | Description |
|---|---|---|---|---|---|
| apiKey | header | string | N/A | No | API key for REST API access (API_KEY or MASTER_KEY) |
| node_id | path | string | No | Node UUID |
Response 200 OK¶
application/json
{
"data": {
"application": {
"memory_binary_bytes": 2097152,
"memory_binary_mb": 2.0,
"memory_ets_bytes": 4194304,
"memory_ets_mb": 4.0,
"memory_processes_bytes": 83886080,
"memory_processes_mb": 80.0,
"memory_total_bytes": 125829120,
"memory_total_mb": 120.0,
"process_count": 342,
"uptime_human": "2d 0h 0m",
"uptime_seconds": 172800
},
"cluster_name": "production",
"commands": {
"completed_total": 150,
"enqueued_total": 152,
"reported_total": 150,
"synced_total": 156
},
"discovery": {
"admins_found_last": 5,
"scans_total": 48
},
"health_check": {
"reports_total": 0
},
"node_id": "550e8400-e29b-41d4-a716-446655440000",
"oban_queues": [
{
"available": 0,
"completed": 245,
"discarded": 1,
"executing": 1,
"queue": "default",
"retryable": 0
},
{
"available": 0,
"completed": 150,
"discarded": 0,
"executing": 0,
"queue": "commands",
"retryable": 0
}
],
"proxy": {
"bytes_down_mb": 900.0,
"bytes_down_total": 943718400,
"bytes_up_mb": 150.0,
"bytes_up_total": 157286400,
"http_blocked_by_reason": {
"agent_port_blocked": 1,
"docker_network_blocked": 4,
"localhost_blocked": 8,
"metadata_service_blocked": 2
},
"http_blocked_total": 15,
"http_connections_total": 523,
"socks5_blocked_by_reason": {
"docker_port_blocked": 2,
"localhost_blocked": 3
},
"socks5_blocked_total": 5,
"socks5_connections_total": 87,
"tunnels_closed_deadline_total": 12,
"tunnels_closed_drain_timeout_total": 3,
"tunnels_closed_normal_total": 590,
"tunnels_closed_total": 605
},
"ssh": {
"authentications_total": 45,
"connections_total": 44
},
"timestamp": "2025-01-15T15:30:00Z",
"vpn": {
"pulls_total": 7
}
}
}
Schema of the response body
{
"description": "Application-level metrics from edge_agent PromEx (BEAM stats, Oban, business metrics).\n",
"example": {
"data": {
"application": {
"memory_binary_bytes": 2097152,
"memory_binary_mb": 2.0,
"memory_ets_bytes": 4194304,
"memory_ets_mb": 4.0,
"memory_processes_bytes": 83886080,
"memory_processes_mb": 80.0,
"memory_total_bytes": 125829120,
"memory_total_mb": 120.0,
"process_count": 342,
"uptime_human": "2d 0h 0m",
"uptime_seconds": 172800
},
"cluster_name": "production",
"commands": {
"completed_total": 150,
"enqueued_total": 152,
"reported_total": 150,
"synced_total": 156
},
"discovery": {
"admins_found_last": 5,
"scans_total": 48
},
"health_check": {
"reports_total": 0
},
"node_id": "550e8400-e29b-41d4-a716-446655440000",
"oban_queues": [
{
"available": 0,
"completed": 245,
"discarded": 1,
"executing": 1,
"queue": "default",
"retryable": 0
},
{
"available": 0,
"completed": 150,
"discarded": 0,
"executing": 0,
"queue": "commands",
"retryable": 0
}
],
"proxy": {
"bytes_down_mb": 900.0,
"bytes_down_total": 943718400,
"bytes_up_mb": 150.0,
"bytes_up_total": 157286400,
"http_blocked_by_reason": {
"agent_port_blocked": 1,
"docker_network_blocked": 4,
"localhost_blocked": 8,
"metadata_service_blocked": 2
},
"http_blocked_total": 15,
"http_connections_total": 523,
"socks5_blocked_by_reason": {
"docker_port_blocked": 2,
"localhost_blocked": 3
},
"socks5_blocked_total": 5,
"socks5_connections_total": 87,
"tunnels_closed_deadline_total": 12,
"tunnels_closed_drain_timeout_total": 3,
"tunnels_closed_normal_total": 590,
"tunnels_closed_total": 605
},
"ssh": {
"authentications_total": 45,
"connections_total": 44
},
"timestamp": "2025-01-15T15:30:00Z",
"vpn": {
"pulls_total": 7
}
}
},
"properties": {
"data": {
"properties": {
"application": {
"description": "Application health and BEAM VM stats",
"properties": {
"memory_binary_bytes": {
"description": "Memory used by binaries in bytes",
"nullable": true,
"type": "integer"
},
"memory_binary_mb": {
"description": "Memory used by binaries in MB",
"nullable": true,
"type": "number"
},
"memory_ets_bytes": {
"description": "Memory used by ETS tables in bytes",
"nullable": true,
"type": "integer"
},
"memory_ets_mb": {
"description": "Memory used by ETS tables in MB",
"nullable": true,
"type": "number"
},
"memory_processes_bytes": {
"description": "Memory used by BEAM processes in bytes",
"nullable": true,
"type": "integer"
},
"memory_processes_mb": {
"description": "Memory used by BEAM processes in MB",
"nullable": true,
"type": "number"
},
"memory_total_bytes": {
"description": "Total BEAM memory allocated in bytes",
"nullable": true,
"type": "integer"
},
"memory_total_mb": {
"description": "Total BEAM memory in MB",
"nullable": true,
"type": "number"
},
"process_count": {
"description": "Number of BEAM processes running",
"nullable": true,
"type": "integer"
},
"uptime_human": {
"description": "Human-readable uptime (e.g., '2d 5h 30m')",
"nullable": true,
"type": "string"
},
"uptime_seconds": {
"description": "Application uptime in seconds",
"nullable": true,
"type": "integer"
}
},
"type": "object"
},
"cluster_name": {
"description": "Name of the cluster this node belongs to",
"type": "string"
},
"commands": {
"description": "Command execution metrics",
"properties": {
"completed_total": {
"description": "Total command executions completed",
"nullable": true,
"type": "integer"
},
"enqueued_total": {
"description": "Total command executions enqueued for local execution",
"nullable": true,
"type": "integer"
},
"reported_total": {
"description": "Total results reported back to admin",
"nullable": true,
"type": "integer"
},
"synced_total": {
"description": "Total command sync calls made to admin",
"nullable": true,
"type": "integer"
}
},
"type": "object"
},
"discovery": {
"description": "Admin discovery metrics",
"properties": {
"admins_found_last": {
"description": "Number of admins discovered during the last scan",
"nullable": true,
"type": "integer"
},
"scans_total": {
"description": "Total discovery scans performed",
"nullable": true,
"type": "integer"
}
},
"type": "object"
},
"health_check": {
"description": "Health check report metrics (only active when VPN is down and agent is in HTTP fallback mode)",
"properties": {
"reports_total": {
"description": "Total health check reports sent to admin via HTTP fallback",
"nullable": true,
"type": "integer"
}
},
"type": "object"
},
"node_id": {
"description": "Node unique identifier",
"format": "uuid",
"type": "string"
},
"oban_queues": {
"description": "Oban job queue states",
"items": {
"properties": {
"available": {
"description": "Jobs available to run",
"type": "integer"
},
"completed": {
"description": "Completed jobs",
"type": "integer"
},
"discarded": {
"description": "Discarded jobs (max retries exceeded)",
"type": "integer"
},
"executing": {
"description": "Jobs currently executing",
"type": "integer"
},
"queue": {
"description": "Queue name (e.g., 'default', 'commands')",
"type": "string"
},
"retryable": {
"description": "Jobs awaiting retry",
"type": "integer"
}
},
"type": "object"
},
"type": "array"
},
"proxy": {
"description": "Proxy server connection and security metrics",
"properties": {
"bytes_down_mb": {
"description": "Cumulative target→client bytes in MB",
"nullable": true,
"type": "number"
},
"bytes_down_total": {
"description": "Cumulative bytes forwarded target→client across all tunnels",
"nullable": true,
"type": "integer"
},
"bytes_up_mb": {
"description": "Cumulative client→target bytes in MB",
"nullable": true,
"type": "number"
},
"bytes_up_total": {
"description": "Cumulative bytes forwarded client→target across all tunnels",
"nullable": true,
"type": "integer"
},
"http_blocked_by_reason": {
"additionalProperties": {
"type": "integer"
},
"description": "HTTP blocked requests grouped by reason",
"example": {
"docker_network_blocked": 3,
"docker_port_blocked": 1,
"localhost_blocked": 5,
"metadata_service_blocked": 2
},
"nullable": true,
"type": "object"
},
"http_blocked_total": {
"description": "Total HTTP requests blocked by security rules",
"nullable": true,
"type": "integer"
},
"http_connections_total": {
"description": "Total HTTP proxy connections",
"nullable": true,
"type": "integer"
},
"socks5_blocked_by_reason": {
"additionalProperties": {
"type": "integer"
},
"description": "SOCKS5 blocked requests grouped by reason",
"example": {
"kubernetes_port_blocked": 2,
"localhost_blocked": 3
},
"nullable": true,
"type": "object"
},
"socks5_blocked_total": {
"description": "Total SOCKS5 requests blocked by security rules",
"nullable": true,
"type": "integer"
},
"socks5_connections_total": {
"description": "Total SOCKS5 proxy connections",
"nullable": true,
"type": "integer"
},
"tunnels_closed_deadline_total": {
"description": "Tunnels force-closed by the total-duration deadline (slowloris defence)",
"nullable": true,
"type": "integer"
},
"tunnels_closed_drain_timeout_total": {
"description": "Tunnels force-closed after exceeding the graceful drain grace window",
"nullable": true,
"type": "integer"
},
"tunnels_closed_normal_total": {
"description": "Tunnels closed normally (both sides EOF'd cleanly)",
"nullable": true,
"type": "integer"
},
"tunnels_closed_total": {
"description": "Total tunnels that reached end-of-life (all close reasons combined)",
"nullable": true,
"type": "integer"
}
},
"type": "object"
},
"ssh": {
"description": "SSH server metrics",
"properties": {
"authentications_total": {
"description": "Total SSH authentication attempts (all methods)",
"nullable": true,
"type": "integer"
},
"connections_total": {
"description": "Total SSH connections established",
"nullable": true,
"type": "integer"
}
},
"type": "object"
},
"timestamp": {
"description": "When the metrics were collected (ISO 8601 format)",
"format": "date-time",
"type": "string"
},
"vpn": {
"description": "VPN connectivity metrics",
"properties": {
"pulls_total": {
"description": "Total VPN config pulls performed (daily backstop for DNS recovery after netclient restart)",
"nullable": true,
"type": "integer"
}
},
"type": "object"
}
},
"type": "object"
}
},
"title": "AgentMetricsResponse",
"type": "object"
}
Response 404 Not Found¶
application/json
{
"error": {
"code": "not_found",
"message": "Resource not found"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "404 Not Found",
"example": {
"error": {
"code": "not_found",
"message": "Resource not found"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "not_found",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Resource not found",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "NotFoundResponse",
"type": "object"
}
Response 503 Service Unavailable¶
application/json
{
"error": {
"code": "service_unavailable",
"message": "Downstream dependency unreachable"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "503 Service Unavailable",
"example": {
"error": {
"code": "service_unavailable",
"message": "Downstream dependency unreachable"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "service_unavailable",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Downstream dependency unreachable",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "ServiceUnavailableResponse",
"type": "object"
}
Commands.Command¶
POST /api/v1/commands¶
Create a new command
Description Create a new command for execution on nodes using flexible targeting options.
Targeting types: - 'all': Target all nodes (with optional filters) - 'nodes': Target specific nodes by IDs (with optional filters) - 'clusters': Target specific clusters by names (with optional filters)
Node and cluster filters can be applied to further refine targeting.
Input parameters
| Parameter | In | Type | Default | Nullable | Description |
|---|---|---|---|---|---|
| apiKey | header | string | N/A | No | API key for REST API access (API_KEY or MASTER_KEY) |
Request body
application/json
{
"command_text": "ABC=value\necho $ABC\nsudo docker ps",
"targeting": {
"node_filters": {
"id_type": "persistent",
"status": "healthy"
},
"node_ids": [
"01234567-89ab-cdef-0123-456789abcdef"
],
"type": "nodes"
}
}
Schema of the request body
{
"description": "Create a new command with flexible targeting options",
"example": {
"command_text": "ABC=value\necho $ABC\nsudo docker ps",
"targeting": {
"node_filters": {
"id_type": "persistent",
"status": "healthy"
},
"node_ids": [
"01234567-89ab-cdef-0123-456789abcdef"
],
"type": "nodes"
}
},
"properties": {
"command_text": {
"description": "Multi-line shell script/commands to execute",
"example": "ABC=value\necho $ABC\nsystemctl restart nginx",
"minLength": 1,
"type": "string"
},
"expired_at": {
"description": "Deadline after which pending/sent executions are automatically expired. Must be in the future. Immutable after creation.",
"example": "2025-12-31T23:59:59Z",
"format": "date-time",
"nullable": true,
"type": "string"
},
"targeting": {
"example": {
"cluster_filters": {
"name": "*prod*"
},
"cluster_names": [
"prod",
"staging"
],
"node_filters": {
"id_type": "persistent",
"status": "healthy"
},
"type": "clusters"
},
"properties": {
"cluster_filters": {
"additionalProperties": false,
"description": "Optional filters to apply to target clusters (AND logic with node_filters). Supports all cluster list filters.",
"properties": {
"has_node_limit": {
"description": "Filter clusters that have (true) or do not have (false) a node limit set",
"type": "boolean"
},
"inserted_at__gte": {
"anyOf": [
{
"format": "date-time",
"type": "string"
},
{
"format": "date",
"type": "string"
}
],
"description": "Filter clusters inserted after or on this date"
},
"inserted_at__lte": {
"anyOf": [
{
"format": "date-time",
"type": "string"
},
{
"format": "date",
"type": "string"
}
],
"description": "Filter clusters inserted before or on this date"
},
"ipv4_range": {
"description": "Filter by IPv4 range (CIDR notation)",
"type": "string"
},
"name": {
"description": "Filter by cluster name (exact match or wildcard: prod*, *staging, etc.)",
"type": "string"
},
"node_count": {
"description": "Filter by exact node count",
"type": "integer"
},
"node_count__gte": {
"description": "Filter by node count greater than or equal to",
"type": "integer"
},
"node_count__lte": {
"description": "Filter by node count less than or equal to",
"type": "integer"
},
"updated_at__gte": {
"anyOf": [
{
"format": "date-time",
"type": "string"
},
{
"format": "date",
"type": "string"
}
],
"description": "Filter clusters updated after or on this datetime"
},
"updated_at__lte": {
"anyOf": [
{
"format": "date-time",
"type": "string"
},
{
"format": "date",
"type": "string"
}
],
"description": "Filter clusters updated before or on this datetime"
}
},
"type": "object"
},
"cluster_names": {
"description": "Array of cluster names (required when type is 'clusters') (will always be deduplicated)",
"example": [
"prod",
"staging"
],
"items": {
"type": "string"
},
"type": "array"
},
"node_filters": {
"additionalProperties": false,
"description": "Optional filters to apply to target nodes (AND logic with cluster_filters). Supports all node list filters except cluster_name.",
"properties": {
"cluster_name": {
"description": "Filter by cluster name (exact match or wildcard: prod*, *staging, etc.)",
"type": "string"
},
"id_type": {
"description": "Filter by node ID type",
"enum": [
"persistent",
"random"
],
"type": "string"
},
"inserted_at__gte": {
"anyOf": [
{
"format": "date-time",
"type": "string"
},
{
"format": "date",
"type": "string"
}
],
"description": "Filter nodes inserted after or on this date"
},
"inserted_at__lte": {
"anyOf": [
{
"format": "date-time",
"type": "string"
},
{
"format": "date",
"type": "string"
}
],
"description": "Filter nodes inserted before or on this date"
},
"last_seen_at__gte": {
"anyOf": [
{
"format": "date-time",
"type": "string"
},
{
"format": "date",
"type": "string"
}
],
"description": "Filter nodes last seen after or on this datetime"
},
"last_seen_at__lte": {
"anyOf": [
{
"format": "date-time",
"type": "string"
},
{
"format": "date",
"type": "string"
}
],
"description": "Filter nodes last seen before or on this datetime"
},
"self_update_enabled": {
"description": "Filter by self-update enabled status",
"type": "boolean"
},
"status": {
"description": "Filter by node status",
"enum": [
"healthy",
"unhealthy",
"unreachable"
],
"type": "string"
},
"updated_at__gte": {
"anyOf": [
{
"format": "date-time",
"type": "string"
},
{
"format": "date",
"type": "string"
}
],
"description": "Filter nodes updated after or on this datetime"
},
"updated_at__lte": {
"anyOf": [
{
"format": "date-time",
"type": "string"
},
{
"format": "date",
"type": "string"
}
],
"description": "Filter nodes updated before or on this datetime"
},
"version": {
"description": "Filter by node version (exact match or wildcard: 1.0.0, 1.*, etc.)",
"type": "string"
}
},
"type": "object"
},
"node_ids": {
"description": "Array of node IDs (required when type is 'nodes') (will always be deduplicated)",
"example": [
"01234567-89ab-cdef-0123-456789abcdef",
"fedcba98-7654-3210-fedc-ba9876543210"
],
"items": {
"format": "uuid",
"type": "string"
},
"type": "array"
},
"type": {
"description": "Targeting strategy: 'all' for all nodes, 'nodes' for specific nodes, 'clusters' for specific clusters",
"enum": [
"all",
"nodes",
"clusters"
],
"type": "string"
}
},
"required": [
"type"
],
"type": "object"
},
"timeout": {
"description": "Command timeout in milliseconds (optional, null or omitted means no timeout, must be > 0)",
"example": 30000,
"minimum": 1,
"nullable": true,
"type": "integer"
}
},
"required": [
"command_text",
"targeting"
],
"title": "CommandCreateRequest",
"type": "object"
}
Response 201 Created¶
application/json
{
"data": {
"command_text": "ABC=value\necho $ABC\nsystemctl restart nginx",
"expired_at": "2025-12-31T23:59:59Z",
"id": "01234567-89ab-cdef-0123-456789abcdef",
"inserted_at": "2025-06-17T10:30:00Z",
"targeting": {
"node_filters": {
"status": "healthy"
},
"node_ids": [
"01234567-89ab-cdef-0123-456789abcdef"
],
"type": "nodes"
},
"timeout": 30000,
"updated_at": "2025-06-17T10:30:00Z"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "Single command response",
"properties": {
"data": {
"$ref": "#/components/schemas/CommandResponse"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"data",
"meta"
],
"title": "CommandSingleResponse",
"type": "object"
}
Response 422 Unprocessable Entity¶
application/json
{
"error": {
"code": "validation_failed",
"details": {
"name": [
"can't be blank"
],
"timeout": [
"must be greater than 0"
]
},
"message": "Validation failed"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "422 Validation Failed",
"example": {
"error": {
"code": "validation_failed",
"details": {
"name": [
"can't be blank"
],
"timeout": [
"must be greater than 0"
]
},
"message": "Validation failed"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "validation_failed",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Validation failed",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "ChangesetErrorResponse",
"type": "object"
}
GET /api/v1/commands¶
List all commands
Description Returns a paginated list of all commands with filtering and sorting
Input parameters
| Parameter | In | Type | Default | Nullable | Description |
|---|---|---|---|---|---|
| apiKey | header | string | N/A | No | API key for REST API access (API_KEY or MASTER_KEY) |
| command_text | query | string | No | Filter by command text (exact match or wildcard: ls, docker*, etc.) | |
| expired_at__gte | query | No | Filter records where expired_at is on or after this datetime (ISO 8601 datetime; date-only is treated as start of day UTC) | ||
| expired_at__lte | query | No | Filter records where expired_at is on or before this datetime (ISO 8601 datetime; date-only is treated as end of day UTC) | ||
| has_expired_at | query | boolean | No | Filter by whether an expiration is set: true returns commands with expired_at, false returns commands without | |
| has_timeout | query | boolean | No | Filter by whether a timeout is set: true returns commands with a timeout, false returns commands without | |
| inserted_at__gte | query | No | Filter records where inserted_at is on or after this datetime (ISO 8601 datetime; date-only is treated as start of day UTC) | ||
| inserted_at__lte | query | No | Filter records where inserted_at is on or before this datetime (ISO 8601 datetime; date-only is treated as end of day UTC) | ||
| order_by | query | string | No | Comma-separated list of fields to sort by | |
| order_directions | query | string | No | Comma-separated list of sort directions (asc/desc) corresponding to order_by fields | |
| page | query | integer | 1 | No | Page number (1-indexed) |
| page_size | query | integer | 20 | No | Items per page |
| timeout__gte | query | integer | No | Filter commands with timeout greater than or equal to this value (milliseconds) | |
| timeout__lte | query | integer | No | Filter commands with timeout less than or equal to this value (milliseconds) | |
| updated_at__gte | query | No | Filter records where updated_at is on or after this datetime (ISO 8601 datetime; date-only is treated as start of day UTC) | ||
| updated_at__lte | query | No | Filter records where updated_at is on or before this datetime (ISO 8601 datetime; date-only is treated as end of day UTC) |
Response 200 OK¶
application/json
{
"data": [
{
"command_text": "ABC=value\necho $ABC\nsystemctl restart nginx",
"expired_at": "2025-12-31T23:59:59Z",
"id": "01234567-89ab-cdef-0123-456789abcdef",
"inserted_at": "2025-06-17T10:30:00Z",
"targeting": {
"node_filters": {
"status": "healthy"
},
"node_ids": [
"01234567-89ab-cdef-0123-456789abcdef"
],
"type": "nodes"
},
"timeout": 30000,
"updated_at": "2025-06-17T10:30:00Z"
}
],
"meta": {
"pagination": {
"has_next": true,
"has_prev": false,
"next_page": 2,
"page": 1,
"page_size": 20,
"prev_page": 0,
"total_count": 150,
"total_pages": 8
},
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "Paginated list of commands with filtering and sorting metadata",
"properties": {
"data": {
"items": {
"$ref": "#/components/schemas/CommandResponse"
},
"type": "array"
},
"meta": {
"$ref": "#/components/schemas/PaginatedMeta"
}
},
"required": [
"data",
"meta"
],
"title": "CommandPaginatedResponse",
"type": "object"
}
Response 400 Bad Request¶
application/json
{
"error": {
"code": "bad_request",
"details": {
"ipv4_range": [
"Invalid format."
],
"name": [
"Invalid format. Expected ~r/^[a-z0-9]/"
]
},
"message": "Invalid request parameters"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "400 Bad Request",
"example": {
"error": {
"code": "bad_request",
"details": {
"ipv4_range": [
"Invalid format."
],
"name": [
"Invalid format. Expected ~r/^[a-z0-9]/"
]
},
"message": "Invalid request parameters"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "bad_request",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Invalid request parameters",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "BadRequestResponse",
"type": "object"
}
GET /api/v1/commands/{id}¶
Get a specific command
Description Returns details for a specific command by ID
Input parameters
| Parameter | In | Type | Default | Nullable | Description |
|---|---|---|---|---|---|
| apiKey | header | string | N/A | No | API key for REST API access (API_KEY or MASTER_KEY) |
| id | path | string | No | Command ID |
Response 200 OK¶
application/json
{
"data": {
"command_text": "ABC=value\necho $ABC\nsystemctl restart nginx",
"expired_at": "2025-12-31T23:59:59Z",
"id": "01234567-89ab-cdef-0123-456789abcdef",
"inserted_at": "2025-06-17T10:30:00Z",
"targeting": {
"node_filters": {
"status": "healthy"
},
"node_ids": [
"01234567-89ab-cdef-0123-456789abcdef"
],
"type": "nodes"
},
"timeout": 30000,
"updated_at": "2025-06-17T10:30:00Z"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "Single command response",
"properties": {
"data": {
"$ref": "#/components/schemas/CommandResponse"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"data",
"meta"
],
"title": "CommandSingleResponse",
"type": "object"
}
Response 400 Bad Request¶
application/json
{
"error": {
"code": "bad_request",
"details": {
"ipv4_range": [
"Invalid format."
],
"name": [
"Invalid format. Expected ~r/^[a-z0-9]/"
]
},
"message": "Invalid request parameters"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "400 Bad Request",
"example": {
"error": {
"code": "bad_request",
"details": {
"ipv4_range": [
"Invalid format."
],
"name": [
"Invalid format. Expected ~r/^[a-z0-9]/"
]
},
"message": "Invalid request parameters"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "bad_request",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Invalid request parameters",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "BadRequestResponse",
"type": "object"
}
Response 404 Not Found¶
application/json
{
"error": {
"code": "not_found",
"message": "Resource not found"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "404 Not Found",
"example": {
"error": {
"code": "not_found",
"message": "Resource not found"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "not_found",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Resource not found",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "NotFoundResponse",
"type": "object"
}
DELETE /api/v1/commands/{id}¶
Delete a command
Description Delete a command and all its related command executions (cascaded deletion).
Only commands where ALL executions are completed can be deleted. Attempting to delete a command with pending or sent executions will return 409.
Input parameters
| Parameter | In | Type | Default | Nullable | Description |
|---|---|---|---|---|---|
| apiKey | header | string | N/A | No | API key for REST API access (API_KEY or MASTER_KEY) |
| id | path | string | No | Command ID |
Response 204 No Content¶
Schema of the response body
Response 400 Bad Request¶
application/json
{
"error": {
"code": "bad_request",
"details": {
"ipv4_range": [
"Invalid format."
],
"name": [
"Invalid format. Expected ~r/^[a-z0-9]/"
]
},
"message": "Invalid request parameters"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "400 Bad Request",
"example": {
"error": {
"code": "bad_request",
"details": {
"ipv4_range": [
"Invalid format."
],
"name": [
"Invalid format. Expected ~r/^[a-z0-9]/"
]
},
"message": "Invalid request parameters"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "bad_request",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Invalid request parameters",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "BadRequestResponse",
"type": "object"
}
Response 404 Not Found¶
application/json
{
"error": {
"code": "not_found",
"message": "Resource not found"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "404 Not Found",
"example": {
"error": {
"code": "not_found",
"message": "Resource not found"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "not_found",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Resource not found",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "NotFoundResponse",
"type": "object"
}
Response 409 Conflict¶
application/json
{
"error": {
"code": "conflict",
"message": "Resource already exists"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "409 Conflict",
"example": {
"error": {
"code": "conflict",
"message": "Resource already exists"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "conflict",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Resource already exists",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "ConflictResponse",
"type": "object"
}
Commands.CommandExecution¶
GET /api/v1/command_executions¶
List command executions
Description Returns a paginated list of command executions with filtering and sorting
Input parameters
| Parameter | In | Type | Default | Nullable | Description |
|---|---|---|---|---|---|
| apiKey | header | string | N/A | No | API key for REST API access (API_KEY or MASTER_KEY) |
| cancelled_at__gte | query | No | Filter records where cancelled_at is on or after this datetime (ISO 8601 datetime; date-only is treated as start of day UTC) | ||
| cancelled_at__lte | query | No | Filter records where cancelled_at is on or before this datetime (ISO 8601 datetime; date-only is treated as end of day UTC) | ||
| cluster_name | query | string | No | Filter by cluster name via node's cluster (exact match or wildcard: prod, staging, etc.) | |
| command_id | query | string | No | Filter by command ID | |
| completed_at__gte | query | No | Filter records where completed_at is on or after this datetime (ISO 8601 datetime; date-only is treated as start of day UTC) | ||
| completed_at__lte | query | No | Filter records where completed_at is on or before this datetime (ISO 8601 datetime; date-only is treated as end of day UTC) | ||
| exit_code | query | integer | No | Filter by exact exit code | |
| exit_code__gte | query | integer | No | Filter executions with exit code greater than or equal to this value (e.g. 1 for all failures) | |
| exit_code__lte | query | integer | No | Filter executions with exit code less than or equal to this value | |
| has_cluster | query | boolean | No | Filter by cluster_id presence (true = cluster-wide executions, false = non-cluster-wide) | |
| has_output | query | boolean | No | Filter by whether output is present: true returns executions with output, false returns executions with no output | |
| inserted_at__gte | query | No | Filter records where inserted_at is on or after this datetime (ISO 8601 datetime; date-only is treated as start of day UTC) | ||
| inserted_at__lte | query | No | Filter records where inserted_at is on or before this datetime (ISO 8601 datetime; date-only is treated as end of day UTC) | ||
| node_id | query | string | No | Filter by node ID | |
| order_by | query | string | No | Comma-separated list of fields to sort by | |
| order_directions | query | string | No | Comma-separated list of sort directions (asc/desc) corresponding to order_by fields | |
| output | query | string | No | Text search in output (exact match or wildcard: error, *failed, etc.) | |
| page | query | integer | 1 | No | Page number (1-indexed) |
| page_size | query | integer | 20 | No | Items per page |
| sent_at__gte | query | No | Filter records where sent_at is on or after this datetime (ISO 8601 datetime; date-only is treated as start of day UTC) | ||
| sent_at__lte | query | No | Filter records where sent_at is on or before this datetime (ISO 8601 datetime; date-only is treated as end of day UTC) | ||
| status | query | string | No | Filter by execution status | |
| target_all | query | boolean | No | Filter by target_all flag | |
| updated_at__gte | query | No | Filter records where updated_at is on or after this datetime (ISO 8601 datetime; date-only is treated as start of day UTC) | ||
| updated_at__lte | query | No | Filter records where updated_at is on or before this datetime (ISO 8601 datetime; date-only is treated as end of day UTC) |
Response 200 OK¶
application/json
{
"data": [
{
"cluster_name": "prod-east",
"command_id": "fedcba98-7654-3210-fedc-ba9876543210",
"command_text": "echo hello\nls -la",
"completed_at": "2025-06-17T10:31:00Z",
"exit_code": 5,
"id": "01234567-89ab-cdef-0123-456789abcdef",
"inserted_at": "2025-06-17T10:30:00Z",
"node_id": "abcdef01-2345-6789-abcd-ef0123456789",
"output": "$ ABC=value\n$ echo $ABC\nvalue\n$ systemctl restart nginx\nFailed to restart nginx.service: Unit not found\n",
"sent_at": "2025-06-17T10:30:00Z",
"status": "completed",
"target_all": false,
"updated_at": "2025-06-17T10:31:00Z"
}
],
"meta": {
"pagination": {
"has_next": true,
"has_prev": false,
"next_page": 2,
"page": 1,
"page_size": 20,
"prev_page": 0,
"total_count": 150,
"total_pages": 8
},
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "Paginated list of command executions with filtering and sorting metadata",
"properties": {
"data": {
"items": {
"$ref": "#/components/schemas/CommandExecutionResponse"
},
"type": "array"
},
"meta": {
"$ref": "#/components/schemas/PaginatedMeta"
}
},
"required": [
"data",
"meta"
],
"title": "CommandExecutionPaginatedResponse",
"type": "object"
}
Response 400 Bad Request¶
application/json
{
"error": {
"code": "bad_request",
"details": {
"ipv4_range": [
"Invalid format."
],
"name": [
"Invalid format. Expected ~r/^[a-z0-9]/"
]
},
"message": "Invalid request parameters"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "400 Bad Request",
"example": {
"error": {
"code": "bad_request",
"details": {
"ipv4_range": [
"Invalid format."
],
"name": [
"Invalid format. Expected ~r/^[a-z0-9]/"
]
},
"message": "Invalid request parameters"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "bad_request",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Invalid request parameters",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "BadRequestResponse",
"type": "object"
}
GET /api/v1/command_executions/{id}¶
Get a specific command execution
Description Returns details for a specific command execution by ID
Input parameters
| Parameter | In | Type | Default | Nullable | Description |
|---|---|---|---|---|---|
| apiKey | header | string | N/A | No | API key for REST API access (API_KEY or MASTER_KEY) |
| id | path | string | No | Command Execution ID |
Response 200 OK¶
application/json
{
"data": {
"cluster_name": "prod-east",
"command_id": "fedcba98-7654-3210-fedc-ba9876543210",
"command_text": "echo hello\nls -la",
"completed_at": "2025-06-17T10:31:00Z",
"exit_code": 5,
"id": "01234567-89ab-cdef-0123-456789abcdef",
"inserted_at": "2025-06-17T10:30:00Z",
"node_id": "abcdef01-2345-6789-abcd-ef0123456789",
"output": "$ ABC=value\n$ echo $ABC\nvalue\n$ systemctl restart nginx\nFailed to restart nginx.service: Unit not found\n",
"sent_at": "2025-06-17T10:30:00Z",
"status": "completed",
"target_all": false,
"updated_at": "2025-06-17T10:31:00Z"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "Single command execution response",
"properties": {
"data": {
"$ref": "#/components/schemas/CommandExecutionResponse"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"data",
"meta"
],
"title": "CommandExecutionSingleResponse",
"type": "object"
}
Response 400 Bad Request¶
application/json
{
"error": {
"code": "bad_request",
"details": {
"ipv4_range": [
"Invalid format."
],
"name": [
"Invalid format. Expected ~r/^[a-z0-9]/"
]
},
"message": "Invalid request parameters"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "400 Bad Request",
"example": {
"error": {
"code": "bad_request",
"details": {
"ipv4_range": [
"Invalid format."
],
"name": [
"Invalid format. Expected ~r/^[a-z0-9]/"
]
},
"message": "Invalid request parameters"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "bad_request",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Invalid request parameters",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "BadRequestResponse",
"type": "object"
}
Response 404 Not Found¶
application/json
{
"error": {
"code": "not_found",
"message": "Resource not found"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "404 Not Found",
"example": {
"error": {
"code": "not_found",
"message": "Resource not found"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "not_found",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Resource not found",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "NotFoundResponse",
"type": "object"
}
DELETE /api/v1/command_executions/{id}¶
Delete a command execution
Description Delete a specific command execution.
Only completed, cancelled, or expired executions can be deleted. Attempting to delete pending or sent executions will return 409.
Input parameters
| Parameter | In | Type | Default | Nullable | Description |
|---|---|---|---|---|---|
| apiKey | header | string | N/A | No | API key for REST API access (API_KEY or MASTER_KEY) |
| id | path | string | No | Command Execution ID |
Response 204 No Content¶
Schema of the response body
Response 400 Bad Request¶
application/json
{
"error": {
"code": "bad_request",
"details": {
"ipv4_range": [
"Invalid format."
],
"name": [
"Invalid format. Expected ~r/^[a-z0-9]/"
]
},
"message": "Invalid request parameters"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "400 Bad Request",
"example": {
"error": {
"code": "bad_request",
"details": {
"ipv4_range": [
"Invalid format."
],
"name": [
"Invalid format. Expected ~r/^[a-z0-9]/"
]
},
"message": "Invalid request parameters"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "bad_request",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Invalid request parameters",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "BadRequestResponse",
"type": "object"
}
Response 404 Not Found¶
application/json
{
"error": {
"code": "not_found",
"message": "Resource not found"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "404 Not Found",
"example": {
"error": {
"code": "not_found",
"message": "Resource not found"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "not_found",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Resource not found",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "NotFoundResponse",
"type": "object"
}
Response 409 Conflict¶
application/json
{
"error": {
"code": "conflict",
"message": "Resource already exists"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "409 Conflict",
"example": {
"error": {
"code": "conflict",
"message": "Resource already exists"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "conflict",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Resource already exists",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "ConflictResponse",
"type": "object"
}
PATCH /api/v1/command_executions/{id}/cancel¶
Cancel a command execution
Description Attempts to cancel a command execution. Behavior depends on current status:
pending: Immediately markedcancelledin the database (command never ran).sent: Sends cancellation request to agent (best-effort, async). The agent is the source of truth — if it already ran the command, it reports back the real result and the execution is markedcompleted. If the agent honoured the cancellation (exit code 143), it is markedcancelled.completed/cancelled: Returns 409 conflict (already terminal).
Returns 200 if cancellation was initiated. For sent executions, poll status to confirm the outcome.
Input parameters
| Parameter | In | Type | Default | Nullable | Description |
|---|---|---|---|---|---|
| apiKey | header | string | N/A | No | API key for REST API access (API_KEY or MASTER_KEY) |
| id | path | string | No | Command Execution ID |
Response 200 OK¶
application/json
{
"data": {
"result": "cancellation request sent"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "Response from command execution cancellation request",
"properties": {
"data": {
"$ref": "#/components/schemas/CancelExecutionData"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"data",
"meta"
],
"title": "CancelExecutionResponse",
"type": "object"
}
Response 404 Not Found¶
application/json
{
"error": {
"code": "not_found",
"message": "Resource not found"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "404 Not Found",
"example": {
"error": {
"code": "not_found",
"message": "Resource not found"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "not_found",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Resource not found",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "NotFoundResponse",
"type": "object"
}
Response 409 Conflict¶
application/json
{
"error": {
"code": "conflict",
"message": "Resource already exists"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "409 Conflict",
"example": {
"error": {
"code": "conflict",
"message": "Resource already exists"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "conflict",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Resource already exists",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "ConflictResponse",
"type": "object"
}
Response 503 Service Unavailable¶
application/json
{
"error": {
"code": "service_unavailable",
"message": "Downstream dependency unreachable"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "503 Service Unavailable",
"example": {
"error": {
"code": "service_unavailable",
"message": "Downstream dependency unreachable"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "service_unavailable",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Downstream dependency unreachable",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "ServiceUnavailableResponse",
"type": "object"
}
Ssh.SshUsername¶
POST /api/v1/nodes/{node_id}/ssh_usernames¶
Create SSH username
Description Create a new SSH username for a specific node, optionally with public keys and/or password
Input parameters
| Parameter | In | Type | Default | Nullable | Description |
|---|---|---|---|---|---|
| apiKey | header | string | N/A | No | API key for REST API access (API_KEY or MASTER_KEY) |
| node_id | path | string | No | Node ID to create SSH username for |
Request body
application/json
{
"password": "MySecurePassword123!",
"public_keys": [
{
"key_name": "laptop",
"public_key": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGQw7Di3fBr2oc2vbZN5YLz8YpJ8PQb5bXwQwe+QgYX8 user@laptop"
},
{
"key_name": "ci",
"public_key": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI... ci@deploy"
}
],
"username": "admin"
}
Schema of the request body
{
"description": "Create a new SSH username for a node, optionally with password and/or public keys",
"example": {
"password": "MySecurePassword123!",
"public_keys": [
{
"key_name": "laptop",
"public_key": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGQw7Di3fBr2oc2vbZN5YLz8YpJ8PQb5bXwQwe+QgYX8 user@laptop"
},
{
"key_name": "ci",
"public_key": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI... ci@deploy"
}
],
"username": "admin"
},
"properties": {
"password": {
"description": "Optional password for username/password SSH authentication (12-128 characters if provided, will be hashed with Argon2)",
"example": "MySecurePassword123!",
"maxLength": 128,
"minLength": 12,
"nullable": true,
"type": "string"
},
"public_keys": {
"description": "Optional array of SSH public keys to create with this username",
"items": {
"properties": {
"key_name": {
"description": "Human-readable name for the SSH key",
"example": "laptop",
"type": "string"
},
"public_key": {
"description": "SSH public key in OpenSSH format",
"example": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGQw7Di3fBr2oc2vbZN5YLz8YpJ8PQb5bXwQwe+QgYX8 user@laptop",
"type": "string"
}
},
"required": [
"key_name",
"public_key"
],
"type": "object"
},
"nullable": true,
"type": "array"
},
"username": {
"description": "SSH username for node access (3-32 characters, must start with letter or underscore, lowercase letters/digits/hyphens/underscores only)",
"example": "admin",
"maxLength": 32,
"minLength": 3,
"pattern": "^[a-z_][a-z0-9_-]*$",
"type": "string"
}
},
"required": [
"username"
],
"title": "SshUsernameCreateRequest",
"type": "object"
}
Response 201 Created¶
application/json
{
"data": {
"has_password": true,
"id": "01234567-89ab-cdef-0123-456789abcdef",
"inserted_at": "2025-06-23T10:30:00Z",
"node_id": "fedcba98-7654-3210-fedc-ba9876543210",
"public_keys": [
{
"id": "fedcba98-7654-3210-fedc-ba9876543210",
"inserted_at": "2025-06-23T10:30:00Z",
"key_name": "laptop",
"public_key": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGQw7Di3fBr2oc2vbZN5YLz8YpJ8PQb5bXwQwe+QgYX8 user@laptop",
"ssh_username_id": "01234567-89ab-cdef-0123-456789abcdef",
"updated_at": "2025-06-23T10:30:00Z"
}
],
"updated_at": "2025-06-23T10:30:00Z",
"username": "admin"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "Single SSH username response",
"properties": {
"data": {
"$ref": "#/components/schemas/SshUsername"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"data",
"meta"
],
"title": "SshUsernameSingleResponse",
"type": "object"
}
Response 404 Not Found¶
application/json
{
"error": {
"code": "not_found",
"message": "Resource not found"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "404 Not Found",
"example": {
"error": {
"code": "not_found",
"message": "Resource not found"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "not_found",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Resource not found",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "NotFoundResponse",
"type": "object"
}
Response 409 Conflict¶
application/json
{
"error": {
"code": "conflict",
"message": "Resource already exists"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "409 Conflict",
"example": {
"error": {
"code": "conflict",
"message": "Resource already exists"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "conflict",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Resource already exists",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "ConflictResponse",
"type": "object"
}
Response 422 Unprocessable Entity¶
application/json
{
"error": {
"code": "validation_failed",
"details": {
"name": [
"can't be blank"
],
"timeout": [
"must be greater than 0"
]
},
"message": "Validation failed"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "422 Validation Failed",
"example": {
"error": {
"code": "validation_failed",
"details": {
"name": [
"can't be blank"
],
"timeout": [
"must be greater than 0"
]
},
"message": "Validation failed"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "validation_failed",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Validation failed",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "ChangesetErrorResponse",
"type": "object"
}
GET /api/v1/ssh_usernames¶
List SSH usernames
Description Returns a paginated list of SSH usernames with filtering and sorting
Input parameters
| Parameter | In | Type | Default | Nullable | Description |
|---|---|---|---|---|---|
| apiKey | header | string | N/A | No | API key for REST API access (API_KEY or MASTER_KEY) |
| cluster_name | query | string | No | Filter by cluster name via node's cluster (exact match or wildcard: prod, east, etc.) | |
| has_password | query | boolean | No | Filter by whether username has password configured | |
| inserted_at__gte | query | No | Filter records where inserted_at is on or after this datetime (ISO 8601 datetime; date-only is treated as start of day UTC) | ||
| inserted_at__lte | query | No | Filter records where inserted_at is on or before this datetime (ISO 8601 datetime; date-only is treated as end of day UTC) | ||
| node_id | query | string | No | Filter by node ID | |
| order_by | query | string | No | Comma-separated list of fields to sort by | |
| order_directions | query | string | No | Comma-separated list of sort directions (asc/desc) corresponding to order_by fields | |
| page | query | integer | 1 | No | Page number (1-indexed) |
| page_size | query | integer | 20 | No | Items per page |
| updated_at__gte | query | No | Filter records where updated_at is on or after this datetime (ISO 8601 datetime; date-only is treated as start of day UTC) | ||
| updated_at__lte | query | No | Filter records where updated_at is on or before this datetime (ISO 8601 datetime; date-only is treated as end of day UTC) | ||
| username | query | string | No | Filter by username (exact match or wildcard: root, admin, etc.) |
Response 200 OK¶
application/json
{
"data": [
{
"has_password": true,
"id": "01234567-89ab-cdef-0123-456789abcdef",
"inserted_at": "2025-06-23T10:30:00Z",
"node_id": "fedcba98-7654-3210-fedc-ba9876543210",
"public_keys": [
{
"id": "fedcba98-7654-3210-fedc-ba9876543210",
"inserted_at": "2025-06-23T10:30:00Z",
"key_name": "laptop",
"public_key": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGQw7Di3fBr2oc2vbZN5YLz8YpJ8PQb5bXwQwe+QgYX8 user@laptop",
"ssh_username_id": "01234567-89ab-cdef-0123-456789abcdef",
"updated_at": "2025-06-23T10:30:00Z"
}
],
"updated_at": "2025-06-23T10:30:00Z",
"username": "admin"
}
],
"meta": {
"pagination": {
"has_next": true,
"has_prev": false,
"next_page": 2,
"page": 1,
"page_size": 20,
"prev_page": 0,
"total_count": 150,
"total_pages": 8
},
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "Paginated list of SSH usernames with filtering and sorting metadata",
"properties": {
"data": {
"items": {
"$ref": "#/components/schemas/SshUsername"
},
"type": "array"
},
"meta": {
"$ref": "#/components/schemas/PaginatedMeta"
}
},
"required": [
"data",
"meta"
],
"title": "SshUsernamePaginatedResponse",
"type": "object"
}
Response 400 Bad Request¶
application/json
{
"error": {
"code": "bad_request",
"details": {
"ipv4_range": [
"Invalid format."
],
"name": [
"Invalid format. Expected ~r/^[a-z0-9]/"
]
},
"message": "Invalid request parameters"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "400 Bad Request",
"example": {
"error": {
"code": "bad_request",
"details": {
"ipv4_range": [
"Invalid format."
],
"name": [
"Invalid format. Expected ~r/^[a-z0-9]/"
]
},
"message": "Invalid request parameters"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "bad_request",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Invalid request parameters",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "BadRequestResponse",
"type": "object"
}
GET /api/v1/ssh_usernames/{id}¶
Get SSH username
Description Get a specific SSH username by ID
Input parameters
| Parameter | In | Type | Default | Nullable | Description |
|---|---|---|---|---|---|
| apiKey | header | string | N/A | No | API key for REST API access (API_KEY or MASTER_KEY) |
| id | path | string | No | SSH username ID |
Response 200 OK¶
application/json
{
"data": {
"has_password": true,
"id": "01234567-89ab-cdef-0123-456789abcdef",
"inserted_at": "2025-06-23T10:30:00Z",
"node_id": "fedcba98-7654-3210-fedc-ba9876543210",
"public_keys": [
{
"id": "fedcba98-7654-3210-fedc-ba9876543210",
"inserted_at": "2025-06-23T10:30:00Z",
"key_name": "laptop",
"public_key": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGQw7Di3fBr2oc2vbZN5YLz8YpJ8PQb5bXwQwe+QgYX8 user@laptop",
"ssh_username_id": "01234567-89ab-cdef-0123-456789abcdef",
"updated_at": "2025-06-23T10:30:00Z"
}
],
"updated_at": "2025-06-23T10:30:00Z",
"username": "admin"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "Single SSH username response",
"properties": {
"data": {
"$ref": "#/components/schemas/SshUsername"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"data",
"meta"
],
"title": "SshUsernameSingleResponse",
"type": "object"
}
Response 400 Bad Request¶
application/json
{
"error": {
"code": "bad_request",
"details": {
"ipv4_range": [
"Invalid format."
],
"name": [
"Invalid format. Expected ~r/^[a-z0-9]/"
]
},
"message": "Invalid request parameters"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "400 Bad Request",
"example": {
"error": {
"code": "bad_request",
"details": {
"ipv4_range": [
"Invalid format."
],
"name": [
"Invalid format. Expected ~r/^[a-z0-9]/"
]
},
"message": "Invalid request parameters"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "bad_request",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Invalid request parameters",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "BadRequestResponse",
"type": "object"
}
Response 404 Not Found¶
application/json
{
"error": {
"code": "not_found",
"message": "Resource not found"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "404 Not Found",
"example": {
"error": {
"code": "not_found",
"message": "Resource not found"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "not_found",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Resource not found",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "NotFoundResponse",
"type": "object"
}
DELETE /api/v1/ssh_usernames/{id}¶
Delete SSH username
Description Delete an SSH username and all associated public keys
Input parameters
| Parameter | In | Type | Default | Nullable | Description |
|---|---|---|---|---|---|
| apiKey | header | string | N/A | No | API key for REST API access (API_KEY or MASTER_KEY) |
| id | path | string | No | SSH username ID |
Response 204 No Content¶
Schema of the response body
Response 400 Bad Request¶
application/json
{
"error": {
"code": "bad_request",
"details": {
"ipv4_range": [
"Invalid format."
],
"name": [
"Invalid format. Expected ~r/^[a-z0-9]/"
]
},
"message": "Invalid request parameters"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "400 Bad Request",
"example": {
"error": {
"code": "bad_request",
"details": {
"ipv4_range": [
"Invalid format."
],
"name": [
"Invalid format. Expected ~r/^[a-z0-9]/"
]
},
"message": "Invalid request parameters"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "bad_request",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Invalid request parameters",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "BadRequestResponse",
"type": "object"
}
Response 404 Not Found¶
application/json
{
"error": {
"code": "not_found",
"message": "Resource not found"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "404 Not Found",
"example": {
"error": {
"code": "not_found",
"message": "Resource not found"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "not_found",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Resource not found",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "NotFoundResponse",
"type": "object"
}
Ssh.SshPublicKey¶
POST /api/v1/ssh_usernames/{ssh_username_id}/ssh_public_keys¶
Create SSH public key
Description Create a new SSH public key for a specific SSH username. The key must be in valid OpenSSH format.
Input parameters
| Parameter | In | Type | Default | Nullable | Description |
|---|---|---|---|---|---|
| apiKey | header | string | N/A | No | API key for REST API access (API_KEY or MASTER_KEY) |
| ssh_username_id | path | string | No | SSH username ID to create public key for |
Request body
application/json
{
"key_name": "laptop-key",
"public_key": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGQw7Di3fBr2oc2vbZN5YLz8YpJ8PQb5bXwQwe+QgYX8 user@laptop"
}
Schema of the request body
{
"description": "Create a new SSH public key for a username. The key must be in valid OpenSSH format.",
"example": {
"key_name": "laptop-key",
"public_key": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGQw7Di3fBr2oc2vbZN5YLz8YpJ8PQb5bXwQwe+QgYX8 user@laptop"
},
"properties": {
"key_name": {
"description": "Human-readable name for the SSH key",
"example": "laptop-key",
"maxLength": 255,
"minLength": 1,
"type": "string"
},
"public_key": {
"description": "SSH public key in OpenSSH format (algorithm base64data [comment]). Supported algorithms: ssh-ed25519 (recommended), ecdsa-sha2-nistp256, ecdsa-sha2-nistp384, ecdsa-sha2-nistp521, ssh-rsa",
"example": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGQw7Di3fBr2oc2vbZN5YLz8YpJ8PQb5bXwQwe+QgYX8 user@laptop",
"pattern": "^(ssh-ed25519|ecdsa-sha2-nistp(?:256|384|521)|ssh-rsa)\\s+([A-Za-z0-9+/]+=*)\\s*(.*)$",
"type": "string"
}
},
"required": [
"public_key",
"key_name"
],
"title": "SshPublicKeyCreateRequest",
"type": "object"
}
Response 201 Created¶
application/json
{
"data": {
"id": "01234567-89ab-cdef-0123-456789abcdef",
"inserted_at": "2025-06-23T10:30:00Z",
"key_name": "laptop-key",
"public_key": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGQw7Di3fBr2oc2vbZN5YLz8YpJ8PQb5bXwQwe+QgYX8 user@laptop",
"ssh_username_id": "fedcba98-7654-3210-fedc-ba9876543210",
"updated_at": "2025-06-23T10:30:00Z"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "Single SSH public key response",
"properties": {
"data": {
"$ref": "#/components/schemas/SshPublicKey"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"data",
"meta"
],
"title": "SshPublicKeySingleResponse",
"type": "object"
}
Response 404 Not Found¶
application/json
{
"error": {
"code": "not_found",
"message": "Resource not found"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "404 Not Found",
"example": {
"error": {
"code": "not_found",
"message": "Resource not found"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "not_found",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Resource not found",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "NotFoundResponse",
"type": "object"
}
Response 409 Conflict¶
application/json
{
"error": {
"code": "conflict",
"message": "Resource already exists"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "409 Conflict",
"example": {
"error": {
"code": "conflict",
"message": "Resource already exists"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "conflict",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Resource already exists",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "ConflictResponse",
"type": "object"
}
Response 422 Unprocessable Entity¶
application/json
{
"error": {
"code": "validation_failed",
"details": {
"name": [
"can't be blank"
],
"timeout": [
"must be greater than 0"
]
},
"message": "Validation failed"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "422 Validation Failed",
"example": {
"error": {
"code": "validation_failed",
"details": {
"name": [
"can't be blank"
],
"timeout": [
"must be greater than 0"
]
},
"message": "Validation failed"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "validation_failed",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Validation failed",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "ChangesetErrorResponse",
"type": "object"
}
GET /api/v1/ssh_public_keys¶
List SSH public keys
Description Returns a paginated list of SSH public keys with filtering and sorting
Input parameters
| Parameter | In | Type | Default | Nullable | Description |
|---|---|---|---|---|---|
| apiKey | header | string | N/A | No | API key for REST API access (API_KEY or MASTER_KEY) |
| cluster_name | query | string | No | Filter by cluster name via node's cluster (exact match or wildcard: prod, east, etc.) | |
| inserted_at__gte | query | No | Filter records where inserted_at is on or after this datetime (ISO 8601 datetime; date-only is treated as start of day UTC) | ||
| inserted_at__lte | query | No | Filter records where inserted_at is on or before this datetime (ISO 8601 datetime; date-only is treated as end of day UTC) | ||
| key_name | query | string | No | Filter by key name (exact match or wildcard: my-key, prod, etc.) | |
| node_id | query | string | No | Filter by node ID | |
| order_by | query | string | No | Comma-separated list of fields to sort by | |
| order_directions | query | string | No | Comma-separated list of sort directions (asc/desc) corresponding to order_by fields | |
| page | query | integer | 1 | No | Page number (1-indexed) |
| page_size | query | integer | 20 | No | Items per page |
| public_key | query | string | No | Filter by public key content (useful for searching email comments: *@example.com) | |
| ssh_username_id | query | string | No | Filter by SSH username ID | |
| updated_at__gte | query | No | Filter records where updated_at is on or after this datetime (ISO 8601 datetime; date-only is treated as start of day UTC) | ||
| updated_at__lte | query | No | Filter records where updated_at is on or before this datetime (ISO 8601 datetime; date-only is treated as end of day UTC) | ||
| username | query | string | No | Filter by SSH username (exact match or wildcard: deploy, admin, etc.) |
Response 200 OK¶
application/json
{
"data": [
{
"id": "01234567-89ab-cdef-0123-456789abcdef",
"inserted_at": "2025-06-23T10:30:00Z",
"key_name": "laptop-key",
"public_key": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGQw7Di3fBr2oc2vbZN5YLz8YpJ8PQb5bXwQwe+QgYX8 user@laptop",
"ssh_username_id": "fedcba98-7654-3210-fedc-ba9876543210",
"updated_at": "2025-06-23T10:30:00Z"
}
],
"meta": {
"pagination": {
"has_next": true,
"has_prev": false,
"next_page": 2,
"page": 1,
"page_size": 20,
"prev_page": 0,
"total_count": 150,
"total_pages": 8
},
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "Paginated list of SSH public keys with filtering and sorting metadata",
"properties": {
"data": {
"items": {
"$ref": "#/components/schemas/SshPublicKey"
},
"type": "array"
},
"meta": {
"$ref": "#/components/schemas/PaginatedMeta"
}
},
"required": [
"data",
"meta"
],
"title": "SshPublicKeyPaginatedResponse",
"type": "object"
}
Response 400 Bad Request¶
application/json
{
"error": {
"code": "bad_request",
"details": {
"ipv4_range": [
"Invalid format."
],
"name": [
"Invalid format. Expected ~r/^[a-z0-9]/"
]
},
"message": "Invalid request parameters"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "400 Bad Request",
"example": {
"error": {
"code": "bad_request",
"details": {
"ipv4_range": [
"Invalid format."
],
"name": [
"Invalid format. Expected ~r/^[a-z0-9]/"
]
},
"message": "Invalid request parameters"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "bad_request",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Invalid request parameters",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "BadRequestResponse",
"type": "object"
}
GET /api/v1/ssh_public_keys/{id}¶
Get SSH public key
Description Get a specific SSH public key by ID
Input parameters
| Parameter | In | Type | Default | Nullable | Description |
|---|---|---|---|---|---|
| apiKey | header | string | N/A | No | API key for REST API access (API_KEY or MASTER_KEY) |
| id | path | string | No | SSH public key ID |
Response 200 OK¶
application/json
{
"data": {
"id": "01234567-89ab-cdef-0123-456789abcdef",
"inserted_at": "2025-06-23T10:30:00Z",
"key_name": "laptop-key",
"public_key": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGQw7Di3fBr2oc2vbZN5YLz8YpJ8PQb5bXwQwe+QgYX8 user@laptop",
"ssh_username_id": "fedcba98-7654-3210-fedc-ba9876543210",
"updated_at": "2025-06-23T10:30:00Z"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "Single SSH public key response",
"properties": {
"data": {
"$ref": "#/components/schemas/SshPublicKey"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"data",
"meta"
],
"title": "SshPublicKeySingleResponse",
"type": "object"
}
Response 400 Bad Request¶
application/json
{
"error": {
"code": "bad_request",
"details": {
"ipv4_range": [
"Invalid format."
],
"name": [
"Invalid format. Expected ~r/^[a-z0-9]/"
]
},
"message": "Invalid request parameters"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "400 Bad Request",
"example": {
"error": {
"code": "bad_request",
"details": {
"ipv4_range": [
"Invalid format."
],
"name": [
"Invalid format. Expected ~r/^[a-z0-9]/"
]
},
"message": "Invalid request parameters"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "bad_request",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Invalid request parameters",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "BadRequestResponse",
"type": "object"
}
Response 404 Not Found¶
application/json
{
"error": {
"code": "not_found",
"message": "Resource not found"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "404 Not Found",
"example": {
"error": {
"code": "not_found",
"message": "Resource not found"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "not_found",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Resource not found",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "NotFoundResponse",
"type": "object"
}
DELETE /api/v1/ssh_public_keys/{id}¶
Delete SSH public key
Description Delete an SSH public key
Input parameters
| Parameter | In | Type | Default | Nullable | Description |
|---|---|---|---|---|---|
| apiKey | header | string | N/A | No | API key for REST API access (API_KEY or MASTER_KEY) |
| id | path | string | No | SSH public key ID |
Response 204 No Content¶
Schema of the response body
Response 400 Bad Request¶
application/json
{
"error": {
"code": "bad_request",
"details": {
"ipv4_range": [
"Invalid format."
],
"name": [
"Invalid format. Expected ~r/^[a-z0-9]/"
]
},
"message": "Invalid request parameters"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "400 Bad Request",
"example": {
"error": {
"code": "bad_request",
"details": {
"ipv4_range": [
"Invalid format."
],
"name": [
"Invalid format. Expected ~r/^[a-z0-9]/"
]
},
"message": "Invalid request parameters"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "bad_request",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Invalid request parameters",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "BadRequestResponse",
"type": "object"
}
Response 404 Not Found¶
application/json
{
"error": {
"code": "not_found",
"message": "Resource not found"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "404 Not Found",
"example": {
"error": {
"code": "not_found",
"message": "Resource not found"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "not_found",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Resource not found",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "NotFoundResponse",
"type": "object"
}
SelfUpdates.Request¶
POST /api/v1/self_update_requests¶
Create a new self-update request
Description Create a new self-update request to trigger agent updates.
The request will be processed asynchronously. Only healthy nodes with self_update_enabled=true will be updated.
Targeting types: - 'all': Target all nodes (with optional filters) - 'nodes': Target specific nodes by IDs (with optional filters) - 'clusters': Target specific clusters by names (with optional filters)
Node and cluster filters can be applied to further refine targeting.
Input parameters
| Parameter | In | Type | Default | Nullable | Description |
|---|---|---|---|---|---|
| apiKey | header | string | N/A | No | API key for REST API access (API_KEY or MASTER_KEY) |
Request body
application/json
This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.Schema of the request body
{
"description": "Create a new self-update request.\n\nUses the same targeting system as commands. Only healthy nodes with self_update_enabled=true will be updated.\n",
"example": {
"targeting": {
"node_filters": {
"version": "0.1.*"
},
"type": "all"
}
},
"properties": {
"targeting": {
"description": "Targeting specification",
"properties": {
"cluster_filters": {
"additionalProperties": false,
"description": "Optional filters to apply to target clusters (AND logic with node_filters). Supports all cluster list filters.",
"properties": {
"has_node_limit": {
"description": "Filter clusters that have (true) or do not have (false) a node limit set",
"type": "boolean"
},
"inserted_at__gte": {
"anyOf": [
{
"format": "date-time",
"type": "string"
},
{
"format": "date",
"type": "string"
}
],
"description": "Filter clusters inserted after or on this date"
},
"inserted_at__lte": {
"anyOf": [
{
"format": "date-time",
"type": "string"
},
{
"format": "date",
"type": "string"
}
],
"description": "Filter clusters inserted before or on this date"
},
"ipv4_range": {
"description": "Filter by IPv4 range (CIDR notation)",
"type": "string"
},
"name": {
"description": "Filter by cluster name (exact match or wildcard: prod*, *staging, etc.)",
"type": "string"
},
"node_count": {
"description": "Filter by exact node count",
"type": "integer"
},
"node_count__gte": {
"description": "Filter by node count greater than or equal to",
"type": "integer"
},
"node_count__lte": {
"description": "Filter by node count less than or equal to",
"type": "integer"
},
"updated_at__gte": {
"anyOf": [
{
"format": "date-time",
"type": "string"
},
{
"format": "date",
"type": "string"
}
],
"description": "Filter clusters updated after or on this datetime"
},
"updated_at__lte": {
"anyOf": [
{
"format": "date-time",
"type": "string"
},
{
"format": "date",
"type": "string"
}
],
"description": "Filter clusters updated before or on this datetime"
}
},
"type": "object"
},
"cluster_names": {
"description": "Cluster names (required if type is 'clusters')",
"items": {
"type": "string"
},
"type": "array"
},
"node_filters": {
"additionalProperties": false,
"description": "Optional filters to apply to target nodes (AND logic with cluster_filters). Supports all node list filters except cluster_name.",
"properties": {
"cluster_name": {
"description": "Filter by cluster name (exact match or wildcard: prod*, *staging, etc.)",
"type": "string"
},
"id_type": {
"description": "Filter by node ID type",
"enum": [
"persistent",
"random"
],
"type": "string"
},
"inserted_at__gte": {
"anyOf": [
{
"format": "date-time",
"type": "string"
},
{
"format": "date",
"type": "string"
}
],
"description": "Filter nodes inserted after or on this date"
},
"inserted_at__lte": {
"anyOf": [
{
"format": "date-time",
"type": "string"
},
{
"format": "date",
"type": "string"
}
],
"description": "Filter nodes inserted before or on this date"
},
"last_seen_at__gte": {
"anyOf": [
{
"format": "date-time",
"type": "string"
},
{
"format": "date",
"type": "string"
}
],
"description": "Filter nodes last seen after or on this datetime"
},
"last_seen_at__lte": {
"anyOf": [
{
"format": "date-time",
"type": "string"
},
{
"format": "date",
"type": "string"
}
],
"description": "Filter nodes last seen before or on this datetime"
},
"self_update_enabled": {
"description": "Filter by self-update enabled status",
"type": "boolean"
},
"status": {
"description": "Filter by node status",
"enum": [
"healthy",
"unhealthy",
"unreachable"
],
"type": "string"
},
"updated_at__gte": {
"anyOf": [
{
"format": "date-time",
"type": "string"
},
{
"format": "date",
"type": "string"
}
],
"description": "Filter nodes updated after or on this datetime"
},
"updated_at__lte": {
"anyOf": [
{
"format": "date-time",
"type": "string"
},
{
"format": "date",
"type": "string"
}
],
"description": "Filter nodes updated before or on this datetime"
},
"version": {
"description": "Filter by node version (exact match or wildcard: 1.0.0, 1.*, etc.)",
"type": "string"
}
},
"type": "object"
},
"node_ids": {
"description": "Node IDs (required if type is 'nodes')",
"items": {
"format": "uuid",
"type": "string"
},
"type": "array"
},
"type": {
"description": "Targeting type",
"enum": [
"all",
"nodes",
"clusters"
],
"type": "string"
}
},
"required": [
"type"
],
"type": "object"
}
},
"required": [
"targeting"
],
"title": "SelfUpdateRequestCreateRequest",
"type": "object"
}
Response 201 Created¶
application/json
{
"data": {
"id": "01234567-89ab-cdef-0123-456789abcdef",
"inserted_at": "2025-06-17T10:30:00Z",
"status": "completed",
"summary": {
"failed": 2,
"total": 10,
"triggered": 8
},
"targeting": {
"node_filters": {
"self_update_enabled": true,
"status": "healthy"
},
"type": "all"
},
"updated_at": "2025-06-17T10:35:00Z"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "Single self-update request response",
"properties": {
"data": {
"$ref": "#/components/schemas/SelfUpdateRequestResponse"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"data",
"meta"
],
"title": "SelfUpdateRequestSingleResponse",
"type": "object"
}
Response 422 Unprocessable Entity¶
application/json
{
"error": {
"code": "validation_failed",
"details": {
"name": [
"can't be blank"
],
"timeout": [
"must be greater than 0"
]
},
"message": "Validation failed"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "422 Validation Failed",
"example": {
"error": {
"code": "validation_failed",
"details": {
"name": [
"can't be blank"
],
"timeout": [
"must be greater than 0"
]
},
"message": "Validation failed"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "validation_failed",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Validation failed",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "ChangesetErrorResponse",
"type": "object"
}
GET /api/v1/self_update_requests¶
List all self-update requests
Description Returns a paginated list of all self-update requests with filtering and sorting
Input parameters
| Parameter | In | Type | Default | Nullable | Description |
|---|---|---|---|---|---|
| apiKey | header | string | N/A | No | API key for REST API access (API_KEY or MASTER_KEY) |
| inserted_at__gte | query | No | Filter records where inserted_at is on or after this datetime (ISO 8601 datetime; date-only is treated as start of day UTC) | ||
| inserted_at__lte | query | No | Filter records where inserted_at is on or before this datetime (ISO 8601 datetime; date-only is treated as end of day UTC) | ||
| order_by | query | string | No | Comma-separated list of fields to sort by | |
| order_directions | query | string | No | Comma-separated list of sort directions (asc/desc) corresponding to order_by fields | |
| page | query | integer | 1 | No | Page number (1-indexed) |
| page_size | query | integer | 20 | No | Items per page |
| status | query | string | No | Filter by status | |
| updated_at__gte | query | No | Filter records where updated_at is on or after this datetime (ISO 8601 datetime; date-only is treated as start of day UTC) | ||
| updated_at__lte | query | No | Filter records where updated_at is on or before this datetime (ISO 8601 datetime; date-only is treated as end of day UTC) |
Response 200 OK¶
application/json
{
"data": [
{
"id": "01234567-89ab-cdef-0123-456789abcdef",
"inserted_at": "2025-06-17T10:30:00Z",
"status": "completed",
"summary": {
"failed": 2,
"total": 10,
"triggered": 8
},
"targeting": {
"node_filters": {
"self_update_enabled": true,
"status": "healthy"
},
"type": "all"
},
"updated_at": "2025-06-17T10:35:00Z"
}
],
"meta": {
"pagination": {
"has_next": true,
"has_prev": false,
"next_page": 2,
"page": 1,
"page_size": 20,
"prev_page": 0,
"total_count": 150,
"total_pages": 8
},
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "Paginated list of self-update requests",
"properties": {
"data": {
"items": {
"$ref": "#/components/schemas/SelfUpdateRequestResponse"
},
"type": "array"
},
"meta": {
"$ref": "#/components/schemas/PaginatedMeta"
}
},
"required": [
"data",
"meta"
],
"title": "SelfUpdateRequestPaginatedResponse",
"type": "object"
}
Response 400 Bad Request¶
application/json
{
"error": {
"code": "bad_request",
"details": {
"ipv4_range": [
"Invalid format."
],
"name": [
"Invalid format. Expected ~r/^[a-z0-9]/"
]
},
"message": "Invalid request parameters"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "400 Bad Request",
"example": {
"error": {
"code": "bad_request",
"details": {
"ipv4_range": [
"Invalid format."
],
"name": [
"Invalid format. Expected ~r/^[a-z0-9]/"
]
},
"message": "Invalid request parameters"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "bad_request",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Invalid request parameters",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "BadRequestResponse",
"type": "object"
}
GET /api/v1/self_update_requests/{id}¶
Get a specific self-update request
Description Returns details for a specific self-update request by ID
Input parameters
| Parameter | In | Type | Default | Nullable | Description |
|---|---|---|---|---|---|
| apiKey | header | string | N/A | No | API key for REST API access (API_KEY or MASTER_KEY) |
| id | path | string | No | Self-update request ID |
Response 200 OK¶
application/json
{
"data": {
"id": "01234567-89ab-cdef-0123-456789abcdef",
"inserted_at": "2025-06-17T10:30:00Z",
"status": "completed",
"summary": {
"failed": 2,
"total": 10,
"triggered": 8
},
"targeting": {
"node_filters": {
"self_update_enabled": true,
"status": "healthy"
},
"type": "all"
},
"updated_at": "2025-06-17T10:35:00Z"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "Single self-update request response",
"properties": {
"data": {
"$ref": "#/components/schemas/SelfUpdateRequestResponse"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"data",
"meta"
],
"title": "SelfUpdateRequestSingleResponse",
"type": "object"
}
Response 400 Bad Request¶
application/json
{
"error": {
"code": "bad_request",
"details": {
"ipv4_range": [
"Invalid format."
],
"name": [
"Invalid format. Expected ~r/^[a-z0-9]/"
]
},
"message": "Invalid request parameters"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "400 Bad Request",
"example": {
"error": {
"code": "bad_request",
"details": {
"ipv4_range": [
"Invalid format."
],
"name": [
"Invalid format. Expected ~r/^[a-z0-9]/"
]
},
"message": "Invalid request parameters"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "bad_request",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Invalid request parameters",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "BadRequestResponse",
"type": "object"
}
Response 404 Not Found¶
application/json
{
"error": {
"code": "not_found",
"message": "Resource not found"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "404 Not Found",
"example": {
"error": {
"code": "not_found",
"message": "Resource not found"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "not_found",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Resource not found",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "NotFoundResponse",
"type": "object"
}
DELETE /api/v1/self_update_requests/{id}¶
Delete a self-update request
Description Delete a self-update request.
Only completed requests can be deleted. Attempting to delete a pending or processing request will return 409.
Input parameters
| Parameter | In | Type | Default | Nullable | Description |
|---|---|---|---|---|---|
| apiKey | header | string | N/A | No | API key for REST API access (API_KEY or MASTER_KEY) |
| id | path | string | No | Self-update request ID |
Response 204 No Content¶
Schema of the response body
Response 400 Bad Request¶
application/json
{
"error": {
"code": "bad_request",
"details": {
"ipv4_range": [
"Invalid format."
],
"name": [
"Invalid format. Expected ~r/^[a-z0-9]/"
]
},
"message": "Invalid request parameters"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "400 Bad Request",
"example": {
"error": {
"code": "bad_request",
"details": {
"ipv4_range": [
"Invalid format."
],
"name": [
"Invalid format. Expected ~r/^[a-z0-9]/"
]
},
"message": "Invalid request parameters"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "bad_request",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Invalid request parameters",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "BadRequestResponse",
"type": "object"
}
Response 404 Not Found¶
application/json
{
"error": {
"code": "not_found",
"message": "Resource not found"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "404 Not Found",
"example": {
"error": {
"code": "not_found",
"message": "Resource not found"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "not_found",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Resource not found",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "NotFoundResponse",
"type": "object"
}
Response 409 Conflict¶
application/json
{
"error": {
"code": "conflict",
"message": "Resource already exists"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "409 Conflict",
"example": {
"error": {
"code": "conflict",
"message": "Resource already exists"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "conflict",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Resource already exists",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "ConflictResponse",
"type": "object"
}
Events.Type¶
GET /api/v1/event_types¶
List event types
Description
Returns the full catalog of event types Edge Core can publish. Use this list to build a webhook's subscribed_events array — every value here is valid, anything else is rejected at create time. Static, code-owned list. See AsyncAPI spec for each event's payload shape.
Input parameters
| Parameter | In | Type | Default | Nullable | Description |
|---|---|---|---|---|---|
| apiKey | header | string | N/A | No | API key for REST API access (API_KEY or MASTER_KEY) |
Response 200 OK¶
application/json
{
"data": [
"edge.enrollment_key.verified",
"edge.node.registered",
"edge.node.reregistered",
"edge.node.version_changed",
"edge.node.status_changed",
"edge.node.cluster_changed",
"edge.node.update_triggered",
"edge.command_execution.created",
"edge.command_execution.sent",
"edge.command_execution.completed",
"edge.command_execution.cancelled",
"edge.command_execution.expired",
"edge.command_execution.pruned",
"edge.ssh_username.verified",
"edge.self_update_request.completed"
],
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "Full catalog of event types Edge Core can publish.",
"properties": {
"data": {
"description": "Full catalog of event types Edge Core can publish, in catalog order.\n\nUse this list to build a webhook's `subscribed_events` array — every\nentry here is a valid value, anything else is rejected at create time.\nThe list is static (code-owned). See [AsyncAPI spec](/asyncdoc) for\neach event's payload shape.\n",
"example": [
"edge.enrollment_key.verified",
"edge.node.registered",
"edge.node.reregistered",
"edge.node.version_changed",
"edge.node.status_changed",
"edge.node.cluster_changed",
"edge.node.update_triggered",
"edge.command_execution.created",
"edge.command_execution.sent",
"edge.command_execution.completed",
"edge.command_execution.cancelled",
"edge.command_execution.expired",
"edge.command_execution.pruned",
"edge.ssh_username.verified",
"edge.self_update_request.completed"
],
"items": {
"type": "string"
},
"type": "array"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"data",
"meta"
],
"title": "EventTypeListResponse",
"type": "object"
}
Events.Webhook¶
POST /api/v1/webhooks¶
Create webhook
Description
Create a webhook subscription. The destination URL is validated against the SSRF deny list at create time. secret (HMAC signing key, >= 32 bytes) and headers are write-only — encrypted at rest and never returned in any GET response. subscribed_events is an explicit list of event types this webhook fires on; the full catalog is documented in the AsyncAPI spec.
Input parameters
| Parameter | In | Type | Default | Nullable | Description |
|---|---|---|---|---|---|
| apiKey | header | string | N/A | No | API key for REST API access (API_KEY or MASTER_KEY) |
Request body
application/json
{
"headers": {
"Authorization": "Bearer xoxb-token"
},
"secret": "a-cryptographically-random-32-byte-secret",
"subscribed_events": [
"edge.node.registered",
"edge.command_execution.completed"
],
"url": "https://example.com/edge-events"
}
Schema of the request body
{
"description": "Create a new webhook subscription. Webhooks are immutable after create —\nto change any field, delete and recreate.\n\n- `url` is SSRF-checked downstream. Loopback, RFC1918, link-local, and\n cloud-metadata IPs/hostnames are denied unless\n `WEBHOOK_ALLOW_PRIVATE_IPS=true` is set on the admin process.\n- `secret` is the HMAC-SHA256 signing key. Stays on the server; receivers\n verify the `X-Edge-Signature` header against their stored copy.\n- `headers` is an arbitrary string→string map stamped on every request\n (e.g. `Authorization: Bearer ...`). Up to 20 entries.\n- `subscribed_events` is an explicit list of event-type strings — no\n wildcards. Each string must be a known event type from the catalog;\n unknown values are rejected at create time.\n\nDelivery retry budget is `WEBHOOK_MAX_ATTEMPTS` (default 3).\n\nThe full event catalog with payload schemas is documented in the\n[AsyncAPI spec](/asyncdoc).\n",
"example": {
"headers": {
"Authorization": "Bearer xoxb-token"
},
"secret": "a-cryptographically-random-32-byte-secret",
"subscribed_events": [
"edge.node.registered",
"edge.command_execution.completed"
],
"url": "https://example.com/edge-events"
},
"properties": {
"headers": {
"additionalProperties": {
"maxLength": 4096,
"type": "string"
},
"description": "Extra HTTP headers. Up to 20 entries; each value up to 4096 characters.",
"example": {
"Authorization": "Bearer xoxb-token",
"X-Custom": "value"
},
"maxProperties": 20,
"type": "object"
},
"secret": {
"description": "HMAC-SHA256 signing key (≥ 32 bytes). Never returned in responses.",
"example": "a-cryptographically-random-32-byte-secret",
"maxLength": 256,
"minLength": 32,
"type": "string"
},
"subscribed_events": {
"description": "Explicit list of event types this webhook subscribes to. Each entry must be a known event type from the catalog. See [AsyncAPI spec](/asyncdoc).",
"example": [
"edge.node.registered",
"edge.command_execution.completed"
],
"items": {
"maxLength": 256,
"type": "string"
},
"maxItems": 20,
"minItems": 1,
"type": "array"
},
"url": {
"description": "Absolute http(s) URL. SSRF-checked downstream.",
"example": "https://example.com/edge-events",
"maxLength": 2048,
"minLength": 1,
"pattern": "^https?://.+",
"type": "string"
}
},
"required": [
"url",
"secret",
"subscribed_events"
],
"title": "WebhookCreateRequest",
"type": "object"
}
Response 201 Created¶
application/json
{
"data": {
"id": "01234567-89ab-cdef-0123-456789abcdef",
"inserted_at": "2026-05-05T10:00:00Z",
"subscribed_events": [
"edge.node.registered",
"edge.command_execution.completed"
],
"updated_at": "2026-05-05T10:00:00Z",
"url": "https://example.com/edge-events"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "Single webhook response",
"properties": {
"data": {
"$ref": "#/components/schemas/Webhook"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"data",
"meta"
],
"title": "WebhookSingleResponse",
"type": "object"
}
Response 422 Unprocessable Entity¶
application/json
{
"error": {
"code": "validation_failed",
"details": {
"name": [
"can't be blank"
],
"timeout": [
"must be greater than 0"
]
},
"message": "Validation failed"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "422 Validation Failed",
"example": {
"error": {
"code": "validation_failed",
"details": {
"name": [
"can't be blank"
],
"timeout": [
"must be greater than 0"
]
},
"message": "Validation failed"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "validation_failed",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Validation failed",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "ChangesetErrorResponse",
"type": "object"
}
GET /api/v1/webhooks¶
List webhooks
Description Returns a paginated list of webhook subscriptions. The full catalog of event types you can subscribe to is documented in the AsyncAPI spec.
Input parameters
| Parameter | In | Type | Default | Nullable | Description |
|---|---|---|---|---|---|
| apiKey | header | string | N/A | No | API key for REST API access (API_KEY or MASTER_KEY) |
| event_type | query | string | No | Returns webhooks whose subscribed_events list contains this event type. Pass a literal event type, e.g. edge.node.registered. See AsyncAPI spec for the catalog. |
|
| inserted_at__gte | query | No | Filter records where inserted_at is on or after this datetime (ISO 8601 datetime; date-only is treated as start of day UTC) | ||
| inserted_at__lte | query | No | Filter records where inserted_at is on or before this datetime (ISO 8601 datetime; date-only is treated as end of day UTC) | ||
| order_by | query | string | No | Comma-separated list of fields to sort by | |
| order_directions | query | string | No | Comma-separated list of sort directions (asc/desc) corresponding to order_by fields | |
| page | query | integer | 1 | No | Page number (1-indexed) |
| page_size | query | integer | 20 | No | Items per page |
| updated_at__gte | query | No | Filter records where updated_at is on or after this datetime (ISO 8601 datetime; date-only is treated as start of day UTC) | ||
| updated_at__lte | query | No | Filter records where updated_at is on or before this datetime (ISO 8601 datetime; date-only is treated as end of day UTC) | ||
| url | query | string | No | Filter by URL (exact match or wildcard: prefix, suffix, substring) |
Response 200 OK¶
application/json
{
"data": [
{
"id": "01234567-89ab-cdef-0123-456789abcdef",
"inserted_at": "2026-05-05T10:00:00Z",
"subscribed_events": [
"edge.node.registered",
"edge.command_execution.completed"
],
"updated_at": "2026-05-05T10:00:00Z",
"url": "https://example.com/edge-events"
}
],
"meta": {
"pagination": {
"has_next": true,
"has_prev": false,
"next_page": 2,
"page": 1,
"page_size": 20,
"prev_page": 0,
"total_count": 150,
"total_pages": 8
},
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "Paginated list of webhooks with filtering and sorting metadata",
"properties": {
"data": {
"items": {
"$ref": "#/components/schemas/Webhook"
},
"type": "array"
},
"meta": {
"$ref": "#/components/schemas/PaginatedMeta"
}
},
"required": [
"data",
"meta"
],
"title": "WebhookPaginatedResponse",
"type": "object"
}
Response 400 Bad Request¶
application/json
{
"error": {
"code": "bad_request",
"details": {
"ipv4_range": [
"Invalid format."
],
"name": [
"Invalid format. Expected ~r/^[a-z0-9]/"
]
},
"message": "Invalid request parameters"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "400 Bad Request",
"example": {
"error": {
"code": "bad_request",
"details": {
"ipv4_range": [
"Invalid format."
],
"name": [
"Invalid format. Expected ~r/^[a-z0-9]/"
]
},
"message": "Invalid request parameters"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "bad_request",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Invalid request parameters",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "BadRequestResponse",
"type": "object"
}
GET /api/v1/webhooks/{id}¶
Get webhook
Description Get a single webhook by ID.
Input parameters
| Parameter | In | Type | Default | Nullable | Description |
|---|---|---|---|---|---|
| apiKey | header | string | N/A | No | API key for REST API access (API_KEY or MASTER_KEY) |
| id | path | string | No | Webhook ID |
Response 200 OK¶
application/json
{
"data": {
"id": "01234567-89ab-cdef-0123-456789abcdef",
"inserted_at": "2026-05-05T10:00:00Z",
"subscribed_events": [
"edge.node.registered",
"edge.command_execution.completed"
],
"updated_at": "2026-05-05T10:00:00Z",
"url": "https://example.com/edge-events"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "Single webhook response",
"properties": {
"data": {
"$ref": "#/components/schemas/Webhook"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"data",
"meta"
],
"title": "WebhookSingleResponse",
"type": "object"
}
Response 400 Bad Request¶
application/json
{
"error": {
"code": "bad_request",
"details": {
"ipv4_range": [
"Invalid format."
],
"name": [
"Invalid format. Expected ~r/^[a-z0-9]/"
]
},
"message": "Invalid request parameters"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "400 Bad Request",
"example": {
"error": {
"code": "bad_request",
"details": {
"ipv4_range": [
"Invalid format."
],
"name": [
"Invalid format. Expected ~r/^[a-z0-9]/"
]
},
"message": "Invalid request parameters"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "bad_request",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Invalid request parameters",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "BadRequestResponse",
"type": "object"
}
Response 404 Not Found¶
application/json
{
"error": {
"code": "not_found",
"message": "Resource not found"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "404 Not Found",
"example": {
"error": {
"code": "not_found",
"message": "Resource not found"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "not_found",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Resource not found",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "NotFoundResponse",
"type": "object"
}
DELETE /api/v1/webhooks/{id}¶
Delete webhook
Description
Permanently delete a webhook. Webhooks are immutable after create — to change any field (URL, secret, headers, subscribed_events) delete and recreate. The retry budget is process-wide (WEBHOOK_MAX_ATTEMPTS env var on the admin), not per-webhook.
Input parameters
| Parameter | In | Type | Default | Nullable | Description |
|---|---|---|---|---|---|
| apiKey | header | string | N/A | No | API key for REST API access (API_KEY or MASTER_KEY) |
| id | path | string | No | Webhook ID |
Response 204 No Content¶
Schema of the response body
Response 404 Not Found¶
application/json
{
"error": {
"code": "not_found",
"message": "Resource not found"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
}
Schema of the response body
{
"description": "404 Not Found",
"example": {
"error": {
"code": "not_found",
"message": "Resource not found"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-15T10:00:00.000Z"
}
},
"properties": {
"error": {
"properties": {
"code": {
"example": "not_found",
"type": "string"
},
"details": {
"nullable": true
},
"message": {
"example": "Resource not found",
"type": "string"
}
},
"required": [
"code",
"message"
],
"type": "object"
},
"meta": {
"$ref": "#/components/schemas/Meta"
}
},
"required": [
"error",
"meta"
],
"title": "NotFoundResponse",
"type": "object"
}
Schemas¶
Admin¶
| Name | Type | Description |
|---|---|---|
| admin_cluster_name | string | Name of the admin cluster this admin belongs to |
| admin_peer_count | integer | Number of other admins this admin meshes with (= total_admins - 1). Derived from current topology. |
| edge_node_capacity | integer | Maximum edge nodes this admin can own. Derived: max_wireguard_peers - admin_peer_count. The cluster-sharding algorithm consumes this value. |
| erlang_node_name | string | Erlang distribution node name |
| id | string | Admin ID (e.g., k7m3n2p9x4j6) |
| last_computed_at | string(date-time) | null | Last time metadata was computed |
| max_wireguard_peers | integer | Operator-configured WireGuard peer budget for this admin. Counts both admin-mesh peers and edge-node peers. |
| name | string | Admin name (e.g., admin-k7m3n2p9x4j6) |
| netmaker_host_id | string | Netmaker host ID for this admin (UUID format) |
| vpn_hostname | string | DNS hostname for this admin |
AdminCluster¶
| Name | Type | Description |
|---|---|---|
| admin_count | integer | Number of admins in this cluster |
| admins | Array<AdminClusterMember> | Admins present in this cluster, sorted by name |
| ipv4_range | string | IPv4 CIDR for the admin cluster network |
| name | string | Admin cluster network name (e.g., admin-cluster-main) |
AdminClusterMember¶
| Name | Type | Description |
|---|---|---|
| ipv4_address | string | null | IPv4 address assigned within the admin cluster CIDR (without prefix length) |
| last_checked_in | string(date-time) | null | Last time this admin's netclient reported in to Netmaker (ISO 8601) |
| name | string | Admin name (e.g., admin-k7m3n2p9x4j6) |
| netmaker_host_id | string | Netmaker host ID (UUID) |
| status | string | null | Netmaker-derived status: online (recent checkin), offline (stale checkin), disconnected (admin disabled) |
| use_static_port | boolean | True when the admin pins WireGuard to a fixed port across restarts |
| vpn_hostname | string | DNS hostname inside the admin cluster network |
| wireguard_ip_address | string | null | IP address WireGuard peers send tunnel packets to (public or LAN-reachable) |
| wireguard_port | integer | null | WireGuard listen port |
AdminClusters¶
Type: Array<AdminCluster>
AdminClustersResponse¶
| Name | Type | Description |
|---|---|---|
| data | AdminClusters | |
| meta | Meta |
AdminMetricsResponse¶
| Name | Type | Description |
|---|---|---|
| data | Properties: application, commands, discovery, event_broker, gateways, membership, metadata, nodes, oban_queues, proxy, quantum, reconciliation, self_updates, ssh, timestamp, vpn, webhook |
AdminResponse¶
| Name | Type | Description |
|---|---|---|
| data | Admin | |
| meta | Meta |
AdminTopologyEntry¶
| Name | Type | Description |
|---|---|---|
| admin_peer_count | integer | Admin-mesh peers for this admin (= total_admins - 1) |
| edge_node_capacity | integer | Edge nodes this admin can own (= max_wireguard_peers - admin_peer_count) |
| erlang_node_name | string | Erlang distribution node name |
| max_wireguard_peers | integer | Operator-configured WireGuard peer budget for this admin |
| name | string | Admin name (e.g., admin-k7m3n2p9x4j6) |
| netmaker_host_id | string | Netmaker host ID for this admin (UUID format) |
| vpn_hostname | string | Netmaker dns hostname |
AgentCommandExecutionPaginatedResponse¶
| Name | Type | Description |
|---|---|---|
| data | Array<Internal.AgentCommandExecutionResponse> | |
| meta | PaginatedMeta |
AgentMetricsResponse¶
| Name | Type | Description |
|---|---|---|
| data | Properties: application, cluster_name, commands, discovery, health_check, node_id, oban_queues, proxy, ssh, timestamp, vpn |
AliasPaginatedResponse¶
| Name | Type | Description |
|---|---|---|
| data | Array<AliasResponse> | |
| meta | PaginatedMeta |
AliasResponse¶
| Name | Type | Description |
|---|---|---|
| cluster_name | string | Name of the cluster |
| id | string(uuid) | Unique alias identifier |
| inserted_at | string(date-time) | Timestamp when the alias was created |
| name | string | Alias name (used in DNS) |
| node_id | string(uuid) | ID of the node this alias belongs to |
| updated_at | string(date-time) | Timestamp when the alias was last updated |
| vpn_hostname | string | Full DNS hostname (FQDN) |
AliasSingleResponse¶
| Name | Type | Description |
|---|---|---|
| data | AliasResponse | |
| meta | Meta |
BadRequestResponse¶
| Name | Type | Description |
|---|---|---|
| error | Properties: code, details, message |
|
| meta | Meta |
CancelExecutionData¶
| Name | Type | Description |
|---|---|---|
| result | string | Cancellation request status message |
CancelExecutionResponse¶
| Name | Type | Description |
|---|---|---|
| data | CancelExecutionData | |
| meta | Meta |
ChangeClusterRequest¶
| Name | Type | Description |
|---|---|---|
| cluster_name | string | Name of the target cluster to move this node to. |
ChangesetErrorResponse¶
| Name | Type | Description |
|---|---|---|
| error | Properties: code, details, message |
|
| meta | Meta |
ClusterCreateRequest¶
| Name | Type | Description |
|---|---|---|
| ipv4_range | string | null | IPv4 CIDR range (auto-generated if not provided) |
| name | string | Cluster name — primary identifier (max 24 chars, lowercase alphanumeric + hyphens) |
| node_limit | integer | null | Maximum nodes allowed in this cluster (null means no limit enforced) |
ClusterPaginatedResponse¶
| Name | Type | Description |
|---|---|---|
| data | Array<ClusterResponse> | |
| meta | PaginatedMeta |
ClusterResponse¶
| Name | Type | Description |
|---|---|---|
| id | string(uuid) | Unique cluster identifier (UUID for database compatibility, use name for API operations) |
| inserted_at | string(date-time) | When the cluster was created |
| ipv4_range | string | IPv4 CIDR range for this cluster |
| name | string | Cluster name - primary identifier used in API operations (max 24 chars, alphanumeric with hyphens) |
| network_name | string | Netmaker network name |
| node_count | integer | Number of nodes in this cluster |
| node_limit | integer | null | Maximum number of nodes allowed in this cluster (null means no limit enforced) |
| nodes | Array<NodeSummary> | Summary of nodes in this cluster |
| updated_at | string(date-time) | When the cluster was last updated |
| vpn_domain | string | DNS domain suffix for nodes in this cluster |
ClusterSingleResponse¶
| Name | Type | Description |
|---|---|---|
| data | ClusterResponse | |
| meta | Meta |
ClusterUpdateRequest¶
| Name | Type | Description |
|---|---|---|
| node_limit | integer | null | Maximum nodes allowed in this cluster (null means no limit enforced) |
CommandCreateRequest¶
| Name | Type | Description |
|---|---|---|
| command_text | string | Multi-line shell script/commands to execute |
| expired_at | string(date-time) | null | Deadline after which pending/sent executions are automatically expired. Must be in the future. Immutable after creation. |
| targeting | Example: {'cluster_filters': {'name': '*prod*'}, 'cluster_names': ['prod', 'staging'], 'node_filters': {'id_type': 'persistent', 'status': 'healthy'}, 'type': 'clusters'} |
|
| timeout | integer | null | Command timeout in milliseconds (optional, null or omitted means no timeout, must be > 0) |
CommandExecutionPaginatedResponse¶
| Name | Type | Description |
|---|---|---|
| data | Array<CommandExecutionResponse> | |
| meta | PaginatedMeta |
CommandExecutionResponse¶
| Name | Type | Description |
|---|---|---|
| cancelled_at | string(date-time) | null | When the command execution was cancelled |
| cluster_name | string | null | The cluster explicitly targeted when this execution was created. Only set when the command used clusters targeting against a single cluster — null for all, nodes, or multi-cluster targeting. |
| command_id | string(uuid) | ID of the command being executed |
| command_text | string | null | The command text being executed (denormalized for convenience) |
| completed_at | string(date-time) | null | When the command execution was completed |
| exit_code | integer | null | Final exit code (0 = success, non-zero = failure) |
| expired_at | string(date-time) | null | The expiration deadline from the parent command (null if no expiration was set) |
| id | string(uuid) | Unique command execution identifier |
| inserted_at | string(date-time) | When the execution was created |
| node_id | string(uuid) | ID of the target node |
| output | string | null | Combined output from command execution |
| sent_at | string(date-time) | null | When the command was sent to the agent |
| status | string | Current execution status |
| target_all | boolean | Whether this execution was created from a system-wide command |
| timeout | integer | null | Command timeout in milliseconds (null means no timeout) |
| updated_at | string(date-time) | When the execution was last updated |
CommandExecutionSingleResponse¶
| Name | Type | Description |
|---|---|---|
| data | CommandExecutionResponse | |
| meta | Meta |
CommandPaginatedResponse¶
| Name | Type | Description |
|---|---|---|
| data | Array<CommandResponse> | |
| meta | PaginatedMeta |
CommandResponse¶
| Name | Type | Description |
|---|---|---|
| command_text | string | Multi-line shell script/commands |
| expired_at | string(date-time) | null | Deadline after which pending/sent executions are automatically expired (immutable after creation) |
| id | string(uuid) | Unique command identifier |
| inserted_at | string(date-time) | When the command was created |
| targeting | Example: {'node_filters': {'status': 'healthy'}, 'node_ids': ['01234567-89ab-cdef-0123-456789abcdef'], 'type': 'nodes'} |
Targeting configuration used when creating this command (informational only, not filterable) |
| timeout | integer | null | Command timeout in milliseconds (optional, null means no timeout) |
| updated_at | string(date-time) | When the command was last updated |
CommandSingleResponse¶
| Name | Type | Description |
|---|---|---|
| data | CommandResponse | |
| meta | Meta |
ConflictResponse¶
| Name | Type | Description |
|---|---|---|
| error | Properties: code, details, message |
|
| meta | Meta |
CreateAliasRequest¶
| Name | Type | Description |
|---|---|---|
| name | string | Alias name (lowercase alphanumeric with hyphens) |
EdgeClusters¶
EdgeClustersResponse¶
| Name | Type | Description |
|---|---|---|
| data | EdgeClusters | |
| meta | Meta |
EnrollmentKeyCreateRequest¶
| Name | Type | Description |
|---|---|---|
| expired_at | string(date-time) | null | Expiry datetime (ISO 8601). Omit or pass null for no expiry. |
| name | string | null | Optional human-readable label for this key. Display only — not used for lookup. |
| uses_remaining | integer | null | Number of uses (must be >= 1). Pass null for unlimited. Omit to use the default of 1. |
EnrollmentKeyPaginatedResponse¶
| Name | Type | Description |
|---|---|---|
| data | Array<EnrollmentKeyResponse> | |
| meta | PaginatedMeta |
EnrollmentKeyResponse¶
| Name | Type | Description |
|---|---|---|
| cluster_name | string | Cluster this key belongs to |
| expired_at | string(date-time) | null | Expiry datetime (ISO 8601). null means never expires. |
| id | string(uuid) | Enrollment key ID |
| inserted_at | string(date-time) | When the enrollment key was created |
| key | string | Enrollment key blob (base64 JSON). Set as ENROLLMENT_KEY on the agent. |
| last_used_at | string(date-time) | null | When the key was last used. null if unused. |
| name | string | null | Optional human-readable label for this key (display only). null if unset. |
| updated_at | string(date-time) | When the enrollment key was last updated |
| uses_remaining | integer | null | Remaining uses. null means unlimited. |
EnrollmentKeySingleResponse¶
| Name | Type | Description |
|---|---|---|
| data | EnrollmentKeyResponse | |
| meta | Meta |
EnrollmentKeyUpdateRequest¶
| Name | Type | Description |
|---|---|---|
| expired_at | string(date-time) | null | Expiry datetime (ISO 8601), or null to remove expiry. Omit to leave unchanged. |
| name | string | null | Human-readable label for this key, or null to clear. Omit to leave unchanged. |
| uses_remaining | integer | null | Positive integer to set a use limit, or null to make the key unlimited. Omit to leave unchanged. |
EventTypeListResponse¶
| Name | Type | Description |
|---|---|---|
| data | Array<string> | Full catalog of event types Edge Core can publish, in catalog order. |
Use this list to build a webhook's subscribed_events array — every
entry here is a valid value, anything else is rejected at create time.
The list is static (code-owned). See AsyncAPI spec for
each event's payload shape.
|
| meta | Meta | |
ForbiddenResponse¶
| Name | Type | Description |
|---|---|---|
| error | Properties: code, details, message |
|
| meta | Meta |
HostMetricsResponse¶
| Name | Type | Description |
|---|---|---|
| cluster_name | string | Name of the cluster this node belongs to |
| cpu | Properties: cores, load_15m, load_1m, load_5m |
CPU metrics |
| disk | Properties: available_bytes, available_gb, total_bytes, total_gb, usage_percent, used_bytes, used_gb |
Disk metrics for root filesystem (/) |
| memory | Properties: available_bytes, available_gb, total_bytes, total_gb, usage_percent, used_bytes, used_gb |
Memory metrics |
| node_id | string(uuid) | Node unique identifier |
| timestamp | string(date-time) | When the metrics were collected (ISO 8601 format) |
| uptime | Properties: human, seconds |
System uptime information |
Meta¶
| Name | Type | Description |
|---|---|---|
| request_id | string | Unique request identifier (mirrors x-request-id response header) |
| timestamp | string(date-time) | ISO 8601 UTC timestamp of when the response was generated |
MyAdminCluster¶
| Name | Type | Description |
|---|---|---|
| degraded | boolean | True when total_nodes exceeds total_edge_capacity |
| name | string | Admin cluster name |
| topology | Array<AdminTopologyEntry> | List of all admins in the cluster |
| total_admins | integer | Total number of admins in the cluster |
| total_edge_capacity | integer | Sum of edge_node_capacity across all admins in this admin cluster. This is the system's total edge-node sharding budget. |
| total_nodes | integer | Total nodes registered in the system across all clusters |
| weak_leader | string | Name of the current weak leader admin (alphabetically first admin ID in the cluster). Best-effort duplicate work reduction — not a strong guarantee. Always populated — defaults to self on bootstrap, updated on first metadata recomputation. |
MyAdminClusterResponse¶
| Name | Type | Description |
|---|---|---|
| data | MyAdminCluster | |
| meta | Meta |
NodePaginatedResponse¶
| Name | Type | Description |
|---|---|---|
| data | Array<NodeResponse> | |
| meta | PaginatedMeta |
NodeResponse¶
| Name | Type | Description |
|---|---|---|
| api_token | string | API token for agent authentication |
| cluster_name | string | Name of the cluster this node belongs to |
| host_metrics_port | integer | Host metrics port (Node Exporter) |
| http_port | integer | HTTP API port |
| http_proxy_port | integer | HTTP proxy port |
| id | string(uuid) | Unique node identifier |
| id_type | string | Type of node identifier (persistent or random) |
| inserted_at | string(date-time) | When the node was created |
| last_seen_at | string(date-time) | null | Last heartbeat timestamp from the node |
| mdns_hostname | string | mDNS hostname — resolvable on the local LAN via multicast DNS |
| netmaker_host_id | string(uuid) | null | Netmaker Host UUID for API operations |
| node_name | string | Human-readable node name (derived from ID) |
| proxy_password | string | Password for proxy authentication (username is always '_') |
| self_update_enabled | boolean | Whether self-updates are enabled |
| socks5_proxy_port | integer | SOCKS5 proxy port |
| ssh_port | integer | SSH port |
| status | string | Current node status |
| updated_at | string(date-time) | When the node was last updated |
| version | string | null | Agent version |
| vpn_hostname | string | DNS hostname for this node |
| wireguard_metrics_port | integer | WireGuard metrics port (WireGuard Exporter) |
NodeSingleResponse¶
| Name | Type | Description |
|---|---|---|
| data | NodeResponse | |
| meta | Meta |
NodeSummary¶
| Name | Type | Description |
|---|---|---|
| id | string | Node ID |
| id_type | string | Node ID type |
| status | string | Node status |
| vpn_hostname | string | DNS hostname for this node |
NotFoundResponse¶
| Name | Type | Description |
|---|---|---|
| error | Properties: code, details, message |
|
| meta | Meta |
OrphanedClusters¶
OrphanedClustersResponse¶
| Name | Type | Description |
|---|---|---|
| data | OrphanedClusters | |
| meta | Meta |
PaginatedMeta¶
| Name | Type | Description |
|---|---|---|
| pagination | Pagination | |
| request_id | string | Unique request identifier |
| timestamp | string(date-time) | ISO 8601 UTC response timestamp |
Pagination¶
| Name | Type | Description |
|---|---|---|
| has_next | boolean | Whether a next page exists |
| has_prev | boolean | Whether a previous page exists |
| next_page | integer | null | Next page number, null when on the last page |
| page | integer | Current page number |
| page_size | integer | Items per page |
| prev_page | integer | null | Previous page number, null when on the first page |
| total_count | integer | Total number of items |
| total_pages | integer | Total number of pages |
SelfUpdateRequestCreateRequest¶
| Name | Type | Description |
|---|---|---|
| targeting | Properties: cluster_filters, cluster_names, node_filters, node_ids, type |
Targeting specification |
SelfUpdateRequestPaginatedResponse¶
| Name | Type | Description |
|---|---|---|
| data | Array<SelfUpdateRequestResponse> | |
| meta | PaginatedMeta |
SelfUpdateRequestResponse¶
| Name | Type | Description |
|---|---|---|
| id | string(uuid) | Unique request identifier |
| inserted_at | string(date-time) | When the request was created |
| status | string | Request processing status |
| summary | Example: {'failed': 2, 'total': 10, 'triggered': 8} |
Summary of results (available after completion) |
| targeting | Example: {'node_filters': {'self_update_enabled': True, 'status': 'healthy'}, 'type': 'all'} |
Targeting configuration (same format as commands) |
| updated_at | string(date-time) | When the request was last updated |
SelfUpdateRequestSingleResponse¶
| Name | Type | Description |
|---|---|---|
| data | SelfUpdateRequestResponse | |
| meta | Meta |
ServiceUnavailableResponse¶
| Name | Type | Description |
|---|---|---|
| error | Properties: code, details, message |
|
| meta | Meta |
SshPublicKey¶
| Name | Type | Description |
|---|---|---|
| id | string(uuid) | Unique SSH public key identifier |
| inserted_at | string(date-time) | When the SSH public key was created |
| key_name | string | Human-readable name for the SSH key |
| public_key | string | SSH public key in OpenSSH format (algorithm base64data [comment]) |
| ssh_username_id | string(uuid) | SSH username this key belongs to |
| updated_at | string(date-time) | When the SSH public key was last updated |
SshPublicKeyCreateRequest¶
| Name | Type | Description |
|---|---|---|
| key_name | string | Human-readable name for the SSH key |
| public_key | string | SSH public key in OpenSSH format (algorithm base64data [comment]). Supported algorithms: ssh-ed25519 (recommended), ecdsa-sha2-nistp256, ecdsa-sha2-nistp384, ecdsa-sha2-nistp521, ssh-rsa |
SshPublicKeyPaginatedResponse¶
| Name | Type | Description |
|---|---|---|
| data | Array<SshPublicKey> | |
| meta | PaginatedMeta |
SshPublicKeySingleResponse¶
| Name | Type | Description |
|---|---|---|
| data | SshPublicKey | |
| meta | Meta |
SshUsername¶
| Name | Type | Description |
|---|---|---|
| has_password | boolean | Whether this username has a password configured for authentication |
| id | string(uuid) | Unique SSH username identifier |
| inserted_at | string(date-time) | When the SSH username was created |
| node_id | string(uuid) | Node this username belongs to |
| public_keys | Array<SshPublicKey> | SSH public keys associated with this username (only included when preloaded) |
| updated_at | string(date-time) | When the SSH username was last updated |
| username | string | SSH username for node access (3-32 characters) |
SshUsernameCreateRequest¶
| Name | Type | Description |
|---|---|---|
| password | string | null | Optional password for username/password SSH authentication (12-128 characters if provided, will be hashed with Argon2) |
| public_keys | Array<Properties: key_name, public_key> |
Optional array of SSH public keys to create with this username |
| username | string | SSH username for node access (3-32 characters, must start with letter or underscore, lowercase letters/digits/hyphens/underscores only) |
SshUsernamePaginatedResponse¶
| Name | Type | Description |
|---|---|---|
| data | Array<SshUsername> | |
| meta | PaginatedMeta |
SshUsernameSingleResponse¶
| Name | Type | Description |
|---|---|---|
| data | SshUsername | |
| meta | Meta |
UnifiedMetricsResponse¶
| Name | Type | Description |
|---|---|---|
| data | Properties: agent, cluster_name, host, node_id, timestamp |
|
| meta | Meta |
Webhook¶
| Name | Type | Description |
|---|---|---|
| id | string(uuid) | Unique webhook identifier |
| inserted_at | string(date-time) | When the webhook was created |
| subscribed_events | Array<string> | Explicit list of event types this webhook fires on. Each entry is a literal event type from the catalog (no wildcards). See AsyncAPI spec for the full catalog. |
| updated_at | string(date-time) | When the webhook was last updated |
| url | string | Destination URL — receives a POST per matching event |
WebhookCreateRequest¶
| Name | Type | Description |
|---|---|---|
| headers | Example: {'Authorization': 'Bearer xoxb-token', 'X-Custom': 'value'} |
Extra HTTP headers. Up to 20 entries; each value up to 4096 characters. |
| secret | string | HMAC-SHA256 signing key (≥ 32 bytes). Never returned in responses. |
| subscribed_events | Array<string> | Explicit list of event types this webhook subscribes to. Each entry must be a known event type from the catalog. See AsyncAPI spec. |
| url | string | Absolute http(s) URL. SSRF-checked downstream. |
WebhookPaginatedResponse¶
| Name | Type | Description |
|---|---|---|
| data | Array<Webhook> | |
| meta | PaginatedMeta |
WebhookSingleResponse¶
| Name | Type | Description |
|---|---|---|
| data | Webhook | |
| meta | Meta |
Security schemes¶
| Name | Type | Scheme | Description |
|---|---|---|---|
| apiKey | http | bearer | API key for REST API access (API_KEY or MASTER_KEY) |
Tags¶
| Name | Description |
|---|---|
| Admins.Metadata | |
| Admins.Metrics | |
| Nodes.Cluster | |
| Nodes.EnrollmentKey | |
| Nodes.Node | |
| Nodes.Alias | |
| Nodes.Metrics | |
| Commands.Command | |
| Commands.CommandExecution | |
| Ssh.SshUsername | |
| Ssh.SshPublicKey | |
| SelfUpdates.Request | |
| Events.Type | |
| Events.Webhook |