Skip to main content

API Reference

The Proxima Console backend exposes a RESTful API under the /api/v1/ prefix. All endpoints return JSON and follow a consistent response envelope format.

Interactive API Explorer

The full API is documented with OpenAPI/Swagger and available as an interactive explorer at Swagger UI wherever debug endpoints are enabled. Use it to browse endpoints, view request/response schemas, and try API calls directly. Note that both /swagger/* and /metrics are served only when PROXIMA_ENABLE_DEBUG_ENDPOINTS=true; they return 404 in environments where debug endpoints are disabled.

Authentication

The API supports two authentication modes on all /api/v1/* routes:

  1. JWT Bearer tokenAuthorization: Bearer <jwt> (user login sessions)
  2. API Key headerX-API-Key: <key> (service accounts)

Auth endpoints under /api/v1/auth/ have their own rate limit (5/min per IP). See the Authentication page for full details on login, MFA, password flows, and session management.

Public endpoints (no authentication required):

  • GET /healthz
  • GET /readyz
  • GET /metrics — only when PROXIMA_ENABLE_DEBUG_ENDPOINTS=true (see note above)
  • POST /api/v1/webhooks/{gitlab,github,argocd}/{token}Webhook receivers (token-authenticated)
  • POST /api/v1/alerts/webhook/alertmanager/{token} — Alertmanager receiver (token-authenticated)
  • POST /api/v1/twilio/voice/twiml/{callID}, /twilio/voice/gather, /twilio/voice/status — Twilio voice webhooks (authenticated by X-Twilio-Signature)

Request Limits

All endpoints that accept a JSON request body enforce a 1 MB size limit. Requests exceeding this limit receive a 413 Request Entity Too Large response. This is enforced globally via the DecodeJSON helper.

Slug Validation

Fields that represent URL-safe identifiers (e.g., client slug, environment slug) must match the following format:

  • Lowercase alphanumeric characters and hyphens only
  • 1–63 characters long
  • Must start and end with an alphanumeric character
  • Pattern: ^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$

Invalid slugs receive a 400 Bad Request response with a descriptive error message.

Response Format

Success (single resource)

{
"data": {
"id": "c5f8a2b0-1234-4abc-9def-567890abcdef",
"name": "Acme Corp",
"created_at": "2025-01-15T10:30:00Z"
}
}

Success (list)

{
"data": [
{ "id": "...", "name": "Acme Corp" },
{ "id": "...", "name": "Globex Inc" }
],
"meta": {
"total": 45,
"page": 1,
"per_page": 20
}
}

Error

{
"error": {
"code": "not_found",
"message": "Client with ID 'abc' not found",
"request_id": "a1b2c3d4-5678-49ab-cdef-0123456789ab"
}
}

Error Codes

CodeHTTP StatusWhen
bad_request400Invalid input, malformed JSON, validation failure
unauthorized401Missing or invalid authentication token
forbidden403Insufficient permissions for the requested operation
mfa_enrollment_required403Account requires MFA enrollment before access is granted
not_found404Requested entity does not exist
conflict409Duplicate entry or conflicting state
request_too_large413Request body exceeds the 1 MB limit
internal_error500Unexpected server error

Pagination

List endpoints support pagination via query parameters:

ParameterDefaultMaxDescription
page1Page number (1-indexed)
per_page20100Items per page

Example:

GET /api/v1/clients?page=2&per_page=50

Filtering and Sorting

List endpoints support filtering and sorting via query parameters:

ParameterExampleDescription
client_id?client_id=abcFilter by client ID
status?status=onlineFilter by status
sort?sort=hostnameField to sort by
order?order=ascSort direction (asc or desc)

Date Format

All dates are returned in UTC using ISO 8601 format:

2025-01-15T10:30:00Z

Endpoints

Clients

List Clients

GET /api/v1/clients

Returns a paginated list of all clients.

Query Parameters: page, per_page, sort, order

Response: 200 OK

{
"data": [
{
"id": "c5f8a2b0-...",
"name": "Acme Corp",
"slug": "acme-corp",
"created_at": "2025-01-15T10:30:00Z",
"updated_at": "2025-01-15T10:30:00Z"
}
],
"meta": { "total": 5, "page": 1, "per_page": 20 }
}

Create Client

POST /api/v1/clients

Request Body:

{
"name": "Acme Corp",
"slug": "acme-corp"
}

Response: 201 Created

{
"data": {
"id": "c5f8a2b0-...",
"name": "Acme Corp",
"slug": "acme-corp",
"created_at": "2025-01-15T10:30:00Z",
"updated_at": "2025-01-15T10:30:00Z"
}
}

Get Client

GET /api/v1/clients/:id

Response: 200 OK

Returns a single client by ID.

Update Client

PUT /api/v1/clients/:id

Request Body:

{
"name": "Acme Corporation"
}

Response: 200 OK

Delete Client

DELETE /api/v1/clients/:id

Response: 204 No Content


Environments

List Environments for a Client

GET /api/v1/clients/:id/environments

Returns all environments belonging to the specified client.

Query Parameters: page, per_page

Response: 200 OK


Hosts

List Hosts for an Environment

GET /api/v1/environments/:id/hosts

Returns all hosts in the specified environment.

Query Parameters: page, per_page, status, sort, order

Response: 200 OK


Host Details

These endpoints return detailed inventory data collected by agents for a specific host.

Services

GET /api/v1/hosts/:id/services

Returns all services running on the host.

Packages

GET /api/v1/hosts/:id/packages

Returns all installed packages on the host.

Ports

GET /api/v1/hosts/:id/ports

Returns all open/listening ports on the host.

Network Interfaces

GET /api/v1/hosts/:id/interfaces

Returns all network interfaces on the host.

Users

GET /api/v1/hosts/:id/users

Returns all system users on the host.

Processes

GET /api/v1/hosts/:id/processes?page=1&per_page=50

Returns the current process list for the host, sorted by CPU usage (descending). Supports pagination via page and per_page query parameters.

Response: 200 OK — Paginated list of processes with PID, name, command, username, state, ppid, threads, cpu_percent, memory_percent, memory_rss_bytes, memory_vms_bytes, and started_at.

Process Metrics (Drill-Down)

GET /api/v1/hosts/:id/processes/:pid/metrics

Returns CPU, memory, RSS, and thread time-series data for a specific process. Only processes that are in the top 20 by CPU usage at collection time have historical metrics in VictoriaMetrics.

Query Parameters:

ParameterRequiredDefaultDescription
process_nameYesProcess name (for disambiguation on PID reuse)
startNo1 hour agoStart time (RFC3339)
endNonowEnd time (RFC3339)

Response: 200 OK

{
"data": {
"cpu_usage": [{ "time": "...", "avg_value": 0.15, "min_value": 0.1, "max_value": 0.2, "sample_count": 1 }],
"memory_usage": [{ "time": "...", "avg_value": 0.05, "min_value": 0.04, "max_value": 0.06, "sample_count": 1 }],
"memory_rss": [{ "time": "...", "avg_value": 52428800, "min_value": 50000000, "max_value": 55000000, "sample_count": 1 }],
"threads": [{ "time": "...", "avg_value": 4, "min_value": 4, "max_value": 4, "sample_count": 1 }]
}
}

Container Metrics (Drill-Down)

GET /api/v1/hosts/:id/containers/:containerID/metrics

Returns CPU, memory, network, and block I/O time-series data for a specific Docker container. Gauge metrics are returned as-is; counter metrics (network, block I/O) are automatically converted to per-second rates.

Query Parameters:

ParameterRequiredDefaultDescription
startNo1 hour agoStart time (RFC3339)
endNonowEnd time (RFC3339)

Response: 200 OK

{
"data": {
"cpu_usage": [],
"memory_used": [],
"memory_limit": [],
"memory_usage": [],
"network_receive": [],
"network_transmit": [],
"block_read": [],
"block_write": []
}
}

Each field contains an array of MetricDataPoint objects with time, avg_value, min_value, max_value, and sample_count.

Security (Composite)

GET /api/v1/hosts/:id/security

Returns a composite security overview for the host, fetching firewall rules, TLS certificates, loaded kernel modules, and security configuration items in parallel via errgroup.

Response: 200 OK

{
"data": {
"firewall_rules": [
{
"id": "...", "host_id": "...", "chain": "INPUT", "rule_number": 1,
"target": "ACCEPT", "protocol": "tcp", "port": "22",
"tool": "iptables", "table_name": "filter",
"rule_text": "-A INPUT -p tcp --dport 22 -j ACCEPT"
}
],
"certificates": [
{
"id": "...", "host_id": "...", "subject": "CN=example.com",
"issuer": "CN=Let's Encrypt Authority X3",
"not_before": "2026-01-01T00:00:00Z", "not_after": "2026-04-01T00:00:00Z",
"fingerprint_sha256": "AB:CD:EF:...", "is_ca": false,
"key_algorithm": "RSA", "key_bits": 2048
}
],
"kernel_modules": [
{
"id": "...", "host_id": "...", "name": "ext4",
"size_bytes": 757760, "instances": 1, "used_by": []
}
],
"security_items": [
{
"id": "...", "host_id": "...", "category": "ssh",
"key": "PermitRootLogin", "value": "no"
}
]
}
}

Cron Jobs

GET /api/v1/hosts/:id/cron-jobs?page=1&per_page=50

Returns scheduled tasks (crontab entries, cron.d files, and systemd timers) for the host. Supports pagination.

Response: 200 OK

{
"data": [
{
"id": "...", "host_id": "...", "username": "root",
"schedule": "0 2 * * *", "command": "/usr/local/bin/backup.sh",
"source": "crontab", "is_active": true
}
],
"meta": { "page": 1, "per_page": 50, "total": 3 }
}

GPU Devices

GET /api/v1/hosts/:id/gpu-devices

Returns GPU hardware devices detected on the host. Only populated for hosts with discrete GPUs (NVIDIA, AMD, Intel).

Response: 200 OK

{
"data": [
{
"id": "...", "host_id": "...", "device_index": 0,
"name": "NVIDIA A100", "vendor": "NVIDIA",
"driver_version": "550.127.05", "memory_total_bytes": 42949672960,
"pci_bus_id": "0000:05:00.0", "architecture": "Ampere"
}
]
}

Metrics

Metrics endpoints return time-series data collected by agents. They are nested under hosts as sub-resources.

List Metric Names

GET /api/v1/hosts/:id/metrics

Returns all distinct metric names collected for a host (e.g. cpu_usage_ratio, disk_used_ratio).

Response: 200 OK

{
"data": ["cpu_usage_ratio", "disk_used_ratio", "memory_used_ratio"]
}

Query Metric Data Points

GET /api/v1/hosts/:id/metrics/:metricName

Returns time-series data points for a specific metric. For ranges ≤6h, returns 1-minute buckets from the raw table. For longer ranges, returns hourly aggregates.

Query Parameters:

ParameterDefaultDescription
start1 hour agoStart time (RFC3339)
endnowEnd time (RFC3339)
labels_key""Filter by labels key (e.g. device=sda1,mount=/)

Response: 200 OK

{
"data": [
{
"time": "2026-02-16T10:00:00Z",
"avg_value": 45.2,
"min_value": 30.1,
"max_value": 62.8,
"sample_count": 4
}
]
}

List Metric Series

GET /api/v1/hosts/:id/metrics/:metricName/series

Returns distinct labels_key values for a metric, enabling discovery of labeled series (e.g. per-disk, per-network-interface).

Response: 200 OK

{
"data": ["", "device=sda1,mount=/", "device=sdb1,mount=/data"]
}

Batch Query Latest Metrics

GET /api/v1/hosts/metrics/latest

Returns the most recent value for each (host, metric) pair. Designed for the hosts list page to fetch utilization data for all online hosts in a single request instead of per-host calls.

Query Parameters:

ParameterRequiredDescription
host_idsYesComma-separated host IDs (UUIDs)
metricsYesComma-separated metric names

The query is bounded to the last hour — only metrics recorded within the past 60 minutes are returned. Input is limited to 100 host IDs and 50 metric names per request.

Response: 200 OK

{
"data": [
{
"host_id": "c5f8a2b0-1234-4abc-9def-567890abcdef",
"metric_name": "cpu_usage_ratio",
"value": 0.725,
"time": "2026-02-16T10:59:00Z"
},
{
"host_id": "c5f8a2b0-1234-4abc-9def-567890abcdef",
"metric_name": "memory_used_ratio",
"value": 0.55,
"time": "2026-02-16T10:59:00Z"
}
]
}

Errors:

  • 400 Bad Request — missing host_ids or metrics, invalid UUID, no valid metric names after parsing, or exceeding input limits (100 hosts, 50 metrics)

Integrations

List All Integrations

GET /api/v1/integrations

Returns all known integration types with host count and status summary.

Get Integration

GET /api/v1/integrations/:id

Returns a single integration type by ID (e.g., postgresql).

List Hosts for an Integration

GET /api/v1/integrations/:id/hosts

Returns all hosts running the specified integration, including per-collector status and runtime configuration.

Response: 200 OK

{
"data": [
{
"id": "hi-abc123",
"host_id": "host-uuid",
"integration_id": "postgresql",
"collector_name": "pg-main",
"status": "ok",
"status_message": "",
"enabled": true,
"config": {
"max_connections": {
"value": "100",
"source": "configuration file",
"category": "Connections and Authentication"
},
"shared_buffers": {
"value": "16384",
"unit": "8kB",
"source": "configuration file",
"category": "Resource Usage / Memory"
}
},
"hostname": "db-01",
"last_seen_at": "2026-02-20T10:00:00Z"
}
]
}

The config field contains runtime configuration parameters collected by the agent (e.g., PostgreSQL pg_settings). It is a JSON object keyed by parameter name, with each value containing value, unit (optional), source, and category fields. The config is updated every 5 minutes and preserved across heartbeats when no new config data is available.

List Integrations for a Host

GET /api/v1/hosts/:id/integrations

Returns all integrations running on a specific host with collector status and config.

List Integrations for a Client

GET /api/v1/clients/:id/integrations

Returns all integrations across all hosts belonging to a client.


Search (PQL)

The search API uses the Proxima Query Language (PQL) — a custom query language for searching across infrastructure entities. PQL is parsed by a recursive descent parser, compiled to an AST, and translated to parameterized SQL. Results are scoped to the authenticated user's accessible clients.

Search Hosts

GET /api/v1/search?q=host.os ~ "Ubuntu*" AND tag.tier = "critical"

Returns a paginated list of hosts matching the PQL query.

Query Parameters:

ParameterDefaultDescription
q— (required)PQL query string
page1Page number
per_page20Items per page (max 100)
formatjsonResponse format: json or csv

Response: 200 OK

{
"data": [
{
"id": "host-uuid",
"hostname": "web-01",
"ip_address": "10.0.1.5",
"os": "Ubuntu 24.04",
"arch": "amd64",
"agent_status": "online",
"tags": { "role": "web" }
}
],
"meta": { "total": 12, "page": 1, "per_page": 20 }
}

When format=csv, returns Content-Type: text/csv with host data as CSV download.

Count Matching Hosts

GET /api/v1/search/count?q=host.agent_status = "online"

Returns only the count of matching hosts (faster than full search).

Response: 200 OK

{ "data": { "count": 42 } }

List Searchable Fields

GET /api/v1/search/fields

Returns all available PQL fields with their prefix, name, type, and supported operators. Useful for building autocomplete UIs.

Response: 200 OK

{
"data": [
{ "prefix": "host", "name": "hostname", "type": "string", "operators": ["=", "!=", "~", "IN", "NOT IN"] },
{ "prefix": "host", "name": "cpu_cores", "type": "number", "operators": ["=", "!=", ">", "<", ">=", "<=", "IN", "NOT IN"] },
{ "prefix": "tag", "name": "*", "type": "string", "operators": ["=", "!=", "~", "IN", "NOT IN", "EXISTS"] }
]
}

Validate PQL Query

POST /api/v1/search/validate

Validates PQL syntax without executing the query.

Request Body:

{ "query": "host.os = \"Ubuntu\"" }

Response: 200 OK

{ "data": { "valid": true, "error": "" } }

Invalid queries return the parse error with position information:

{ "data": { "valid": false, "error": "expected value at position 12, got EOF" } }

Aggregate Results

GET /api/v1/search/aggregate?q=host.agent_status = "online"&group_by=host.os

Groups matching hosts by a field and returns counts.

Query Parameters:

ParameterRequiredDescription
qYesPQL query string
group_byYesField to group by (must be a host.* field)

Response: 200 OK

{
"data": [
{ "value": "Ubuntu 24.04", "count": 15 },
{ "value": "Debian 12", "count": 8 }
]
}

PQL Quick Reference

Field prefixes: host.*, label.* (agent labels from hosts.tags), tag.* (user-defined entity tags), service.*, container.*, package.*

Operators: =, !=, ~ (glob wildcard — * matches any chars), >, <, >=, <=, IN (...), NOT IN (...), EXISTS

Combinators: AND, OR, NOT, parenthesized grouping ()

Examples:

host.os = "Ubuntu 24.04"
host.arch IN ("amd64", "arm64")
host.hostname ~ "web-*"
service.name = "nginx" AND host.agent_status = "online"
label.role = "web" AND tag.tier = "production"
host.cpu_cores > 4 AND host.memory_total_bytes > 8000000000
NOT tag.owner EXISTS
(service.name = "postgresql" OR service.name = "mysql") AND host.os ~ "Ubuntu*"

Saved Searches

Save PQL queries for quick access. Users see their own searches plus shared searches.

List Saved Searches

GET /api/v1/saved-searches?page=1&per_page=20

Returns the current user's saved searches plus all shared searches.

Response: 200 OK

{
"data": [
{
"id": "search-uuid",
"user_id": "user-uuid",
"name": "Ubuntu production servers",
"query": "host.os ~ \"Ubuntu*\" AND tag.tier = \"production\"",
"description": "All Ubuntu hosts tagged as production tier",
"is_shared": true,
"created_at": "2026-02-26T10:00:00Z",
"updated_at": "2026-02-26T10:00:00Z"
}
],
"meta": { "total": 3, "page": 1, "per_page": 20 }
}
POST /api/v1/saved-searches

Saves a PQL query. The query is validated before saving.

Request Body:

{
"name": "Ubuntu production servers",
"query": "host.os ~ \"Ubuntu*\" AND tag.tier = \"production\"",
"description": "All Ubuntu hosts tagged as production tier",
"is_shared": true
}

Response: 201 Created

GET /api/v1/saved-searches/:id

Returns a saved search by ID. Private (non-shared) searches are only visible to the owner and super admins.

Response: 200 OK

PUT /api/v1/saved-searches/:id

Updates a saved search. Only the owner or a super admin can update.

Request Body: Same as create.

Response: 200 OK

DELETE /api/v1/saved-searches/:id

Deletes a saved search. Only the owner or a super admin can delete.

Response: 204 No Content


Admin: Users

List Users

GET /api/v1/users

Returns a paginated list of all users. Requires users:read permission.

Query Parameters: page, per_page

Response: 200 OK

{
"data": [
{
"id": "user-uuid",
"email": "[email protected]",
"display_name": "Jane Doe",
"status": "active",
"is_super_admin": false,
"mfa_enabled": true,
"last_login_at": "2026-02-23T10:00:00Z",
"created_at": "2026-01-01T00:00:00Z"
}
],
"meta": { "total": 12, "page": 1, "per_page": 20 }
}

Create User

POST /api/v1/users

Creates a new user with status invited. If an email sender is configured, sends an invitation email with a token link. Requires users:write permission.

Request Body:

{
"email": "[email protected]",
"display_name": "New User"
}

Response: 201 Created

Get User

GET /api/v1/users/:id

Returns a user with their team memberships and direct client assignments. Requires users:read permission.

Response: 200 OK

{
"data": {
"id": "user-uuid",
"email": "[email protected]",
"display_name": "Jane Doe",
"status": "active",
"teams": [
{ "team_id": "...", "team_name": "Ops Team", "role_id": "...", "role_name": "Editor" }
],
"clients": [
{ "client_id": "...", "client_name": "Acme", "client_slug": "acme", "role_id": "...", "role_name": "Viewer" }
]
}
}

Update User

PUT /api/v1/users/:id

Updates user fields (display_name, is_super_admin, status). Requires users:write permission.

Delete (Disable) User

DELETE /api/v1/users/:id

Sets user status to disabled, revokes all sessions, and invalidates the access cache. Requires users:delete permission.

Response: 204 No Content

Resend Invitation

POST /api/v1/users/:id/resend-invite

Resends the invitation email. Only works for users with status invited. Requires users:write permission.

Reset Password

POST /api/v1/users/:id/reset-password

Generates a password reset token and sends a reset email. Requires users:write permission and an email sender to be configured.

User Client Assignments

Direct user-to-client assignments (bypassing team-based access):

MethodPathDescription
GET/api/v1/users/:id/clientsList direct client assignments
POST/api/v1/users/:id/clientsAssign user to client with role
PUT/api/v1/users/:id/clients/:clientIdChange assignment role
DELETE/api/v1/users/:id/clients/:clientIdRemove assignment

Admin: Teams

List Teams

GET /api/v1/teams

Returns a paginated list of all teams. Requires teams:read permission.

Create Team

POST /api/v1/teams

Request Body:

{
"name": "Ops Team",
"description": "Operations team"
}

Response: 201 Created

Get Team

GET /api/v1/teams/:id

Returns a team with its members and client assignments.

Response: 200 OK

{
"data": {
"id": "team-uuid",
"name": "Ops Team",
"description": "Operations team",
"members": [
{ "user_id": "...", "email": "[email protected]", "display_name": "Jane", "role_id": "...", "role_name": "Editor" }
],
"clients": [
{ "client_id": "...", "name": "Acme Corp", "slug": "acme" }
]
}
}

Update Team

PUT /api/v1/teams/:id

Delete Team

DELETE /api/v1/teams/:id

Deletes the team and cascades to team_members and team_clients. Invalidates access cache for all former members.

Response: 204 No Content

Team Members

MethodPathDescription
POST/api/v1/teams/:id/membersAdd member (user_id, role_id)
PUT/api/v1/teams/:id/members/:userIdChange member's role (role_id)
DELETE/api/v1/teams/:id/members/:userIdRemove member

Team Clients

MethodPathDescription
POST/api/v1/teams/:id/clientsAdd client (client_id)
DELETE/api/v1/teams/:id/clients/:clientIdRemove client

Admin: Roles

List Roles

GET /api/v1/roles

Returns all roles (system roles listed first). Requires roles:read permission.

Create Role

POST /api/v1/roles

Request Body:

{
"name": "Custom Viewer",
"description": "Read-only access to hosts and metrics",
"permissions": ["hosts:read", "metrics:read"]
}

Permissions are validated against the registry. System roles cannot be created via the API.

Response: 201 Created

Get Role

GET /api/v1/roles/:id

Update Role

PUT /api/v1/roles/:id

System roles (is_system=true) cannot be updated. After update, all users assigned to this role have their access cache invalidated.

Delete Role

DELETE /api/v1/roles/:id

System roles cannot be deleted. Response: 204 No Content

List Permissions

GET /api/v1/permissions

Returns all available permissions in the system (resource:action pairs).


Admin: Service Accounts

List Service Accounts

GET /api/v1/service-accounts

Returns a paginated list. The api_key_hash field is never exposed.

Create Service Account

POST /api/v1/service-accounts

Request Body:

{
"name": "CI Deploy",
"description": "CI pipeline service account",
"role_id": "role-uuid",
"client_scope": ["client-uuid-1", "client-uuid-2"],
"expires_at": "2027-01-01T00:00:00Z"
}

Generates a prxm_-prefixed API key. The plaintext key is returned once in the response — store it securely.

Response: 201 Created

{
"data": {
"id": "sa-uuid",
"name": "CI Deploy",
"api_key": "prxm_base64url-encoded-key",
"api_key_prefix": "prxm_base64u",
"created_at": "2026-02-23T10:00:00Z"
}
}

Get / Update / Delete Service Account

GET    /api/v1/service-accounts/:id
PUT /api/v1/service-accounts/:id
DELETE /api/v1/service-accounts/:id

Delete performs a soft-delete (sets is_active=false).


Admin: Audit Log

Query Audit Log

GET /api/v1/audit-log

Returns a paginated, filterable list of audit log entries. Requires audit:read permission.

Query Parameters:

ParameterTypeDescription
actor_idUUIDFilter by actor
actionstringFilter by action (e.g., user.login, role.create)
resource_typestringFilter by resource type (e.g., user, team, role)
resource_idstringFilter by resource ID
fromRFC3339Start time
toRFC3339End time
pageintPage number
per_pageintItems per page

Authentication

Auth endpoints are documented in detail on the Authentication page. Quick reference:

Public Auth Routes (rate limited: 5/min per IP)

MethodPathDescription
POST/api/v1/auth/loginLogin with email and password
POST/api/v1/auth/refreshRefresh access token (rotate refresh token)
POST/api/v1/auth/mfa/verifyVerify MFA code during login
POST/api/v1/auth/forgot-passwordRequest password reset email
POST/api/v1/auth/reset-passwordReset password with token
POST/api/v1/auth/accept-inviteAccept invitation and set password

Protected Auth Routes (JWT required)

MethodPathDescription
POST/api/v1/auth/logoutLogout (revoke session)
GET/api/v1/auth/meGet current user profile + permissions
POST/api/v1/auth/mfa/setupStart MFA setup (returns TOTP secret + recovery codes)
POST/api/v1/auth/mfa/confirmConfirm MFA setup with TOTP code
DELETE/api/v1/auth/mfaDisable MFA (requires password)
PUT/api/v1/auth/passwordChange password
GET/api/v1/auth/sessionsList active sessions
DELETE/api/v1/auth/sessions/:idRevoke a specific session
DELETE/api/v1/auth/sessionsRevoke all sessions

Health Endpoints

These endpoints do not require authentication.

Liveness

GET /healthz

Returns 200 OK if the backend process is alive.

{ "status": "ok" }

Readiness

GET /readyz

Returns 200 OK if all dependencies (PostgreSQL and NATS) are connected and healthy. Returns 503 Service Unavailable if any dependency is down.

{ "status": "ready" }

Metrics

GET /metrics

Returns Prometheus-formatted metrics for scraping.


Watch Config

Manage per-host file watch configuration. Changes are persisted to the database and pushed to agents in real-time via the NATS command channel.

Get Watch Config

GET /api/v1/hosts/:hostID/watch-config

Returns the current watch config for a host, or 404 if no config has been set.

Response (200):

{
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"host_id": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
"config_type": "watch_files",
"config_data": {
"paths": ["/etc", "/opt/myapp/config"],
"exclude_patterns": ["*.swp", "*.bak"],
"sensitive_files": ["*shadow*", "*.key"],
"max_file_size": 1048576
},
"version": 3,
"created_at": "2026-02-15T10:30:00Z",
"updated_at": "2026-03-01T14:20:00Z"
}
}

Response (404): No watch config set for this host.

Update Watch Config

PUT /api/v1/hosts/:hostID/watch-config

Creates or updates the watch config for a host. The config is saved to the database and published to the agent's command channel for immediate application.

Request body:

{
"paths": ["/etc", "/opt/myapp/config"],
"exclude_patterns": ["*.swp", "*.bak", "*.tmp"],
"sensitive_files": ["*shadow*", "*.key", "*.pem"],
"max_file_size": 1048576
}
FieldTypeRequiredDescription
pathsstring[]YesFilesystem paths to monitor (must be non-empty)
exclude_patternsstring[]NoGlob patterns for files to exclude
sensitive_filesstring[]NoGlob patterns for sensitive files (tracked but content not sent)
max_file_sizeintNoMaximum file size in bytes (default: 1 MB)

Response (200): Returns the saved config (same format as GET).

Response (400): paths field is empty or missing.

Side effect: On successful save, the backend publishes a command to proxima.system.commands.{agentID} (keyed by the agent ID, not the host ID — these are distinct values, and the agent's NATS JWT only permits subscribing to its own per-agent command subject):

{
"type": "config_update",
"config_type": "watch_files",
"version": 3,
"payload": { "paths": [...], "exclude_patterns": [...], ... }
}

If the agent is online, it applies the new config immediately. If offline, the agent will fetch the latest config on next startup.


Events Timeline

Get Event Timeline

GET /api/v1/events/timeline?start=2026-03-01T00:00:00Z&end=2026-03-02T00:00:00Z&host_id=<uuid>

Returns aggregated events for use as chart markers or timeline overlays.

Query Parameters:

ParameterTypeRequiredDescription
startRFC3339YesStart of time range
endRFC3339YesEnd of time range
host_idUUIDNoFilter events for a specific host
environment_idUUIDNoFilter events for a specific environment
event_typestringNoFilter by type: deploy, file_change, agent_status
limitintNoMaximum number of events (default 100)

Response (200):

{
"data": [
{
"timestamp": "2026-03-01T10:30:00Z",
"event_type": "deploy",
"title": "Config updated",
"description": "Agent config v3 applied",
"host_id": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
"environment_id": "c5f8a2b0-1234-4abc-9def-567890abcdef"
}
]
}

MetricsQL Query

Execute Query

POST /api/v1/metrics/query

Executes a MetricsQL expression against VictoriaMetrics with automatic tenant-scoped filtering. The backend injects extra_filters[] based on the authenticated user's client access to enforce tenant isolation.

Request body:

{
"query": "rate(cpu_usage_ratio[5m])",
"start": "2026-03-01T00:00:00Z",
"end": "2026-03-01T01:00:00Z",
"step": "60s",
"host_id": "6ba7b810-9dad-11d1-80b4-00c04fd430c8"
}

host_id and environment_id are top-level fields (there is no scope wrapper), and at least one is required for scope resolution.

FieldTypeRequiredDescription
querystringYesMetricsQL expression
startRFC3339YesStart of time range
endRFC3339YesEnd of time range
stepstringNoQuery resolution step (e.g. 60s, 5m)
host_idUUIDConditionalScope to one host. At least one of host_id / environment_id is required.
environment_idUUIDConditionalScope to one environment. At least one of host_id / environment_id is required.

Response: Streams Prometheus-format JSON (same schema as VictoriaMetrics /api/v1/query_range).


Annotations

Create Annotation

POST /api/v1/annotations

Creates a metric annotation attached to a host at a specific point in time.

Request body:

{
"host_id": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
"timestamp": "2026-03-01T10:30:00Z",
"text": "Deployment v2.3.1 completed"
}
FieldTypeRequiredDescription
host_idUUIDYesHost to attach annotation to
timestampRFC3339YesPoint-in-time for the annotation
textstringYesAnnotation text

Response (201): Returns the created annotation object.

List Annotations

GET /api/v1/annotations?host_id=<uuid>&start=<RFC3339>&end=<RFC3339>

Returns annotations matching the filter criteria.

Query Parameters:

ParameterTypeRequiredDescription
host_idUUIDNoFilter by host
environment_idUUIDNoFilter by environment
startRFC3339NoStart of time range
endRFC3339NoEnd of time range

Response (200): Array of annotation objects.

Delete Annotation

DELETE /api/v1/annotations/:id

Deletes an annotation. Only the annotation creator or a super admin can delete annotations.

Response (204): No content on success.

Response (403): Caller is not the annotation creator or a super admin.


Agent Enrollment

Agent enrollment is handled via HTTPS, not NATS. Agents use one-time install tokens and a challenge-response protocol to obtain NATS credentials. See NATS Security -- Agent Enrollment for the full protocol.

Agent Config Fetch

Config fetch is handled via NATS request-reply, not the REST API. Each agent sends a request to its own per-agent subject:

proxima.system.agent.config.{agentID}

An agent's NATS user JWT only permits publishing to its own per-agent subject, so the backend derives the requesting agent's identity from the broker-enforced subject (the trailing {agentID} token) and never trusts an agent_id supplied in the request body. The backend's ConfigFetchWorker responds with all config types for the requesting agent's host. See Agent -- Config Fetch at Startup for details.