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 token — Authorization: Bearer <jwt> (user login sessions)
  2. API Key header — X-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
page1—Page 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_nameYes—Process 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 hard delete — the row is permanently removed from the database and the account's API key stops authenticating on the very next request. This cannot be undone.

To disable an account reversibly, PUT it with is_active=false instead: the account and its key are retained, the key stops working, and setting is_active=true restores it.


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​

Chart notes. Full semantics (scopes, inheritance, who can edit/delete, markers): Chart Notes & Markers.

Create Annotation​

POST /api/v1/annotations

Creates a note on a host, an environment (shown on every host in it) or a client (shown on every chart under it). Requires annotations:write at the note's scope — env-aware for host/environment notes, client-wide for a client note — and a signed-in user: API-key / service-account callers get 403 "notes need a signed-in user".

Request body:

{
"scope": "environment",
"environment_id": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
"kind": "maintenance",
"timestamp": "2026-03-01T10:30:00Z",
"ends_at": "2026-03-01T11:00:00Z",
"text": "Kernel upgrade window"
}
FieldTypeRequiredDescription
scopehost | environment | clientYesImmutable after create
host_id / environment_id / client_idUUIDThe one matching scopeThe annotated entity
kindnote | maintenance | deployNoDefault note
timestampRFC3339YesPoint in time, or range start
ends_atRFC3339NoMakes the note a range; must be after timestamp
textstringYes1–500 characters (runes)

Response (201): the created note (id, scope, kind, client_id, environment_id, host_id, timestamp, ends_at, text, created_by, created_by_name, created_at, updated_at). Audited as annotation_created.

List Annotations​

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

Returns the notes a chart inherits: a host chart gets its host's notes plus its environment's and client's; an environment chart its own, its hosts' and its client's; a client chart all of the client's. Point notes inside [start, end] and range notes overlapping it; at most 500. Requires metrics:read on the target; rows are further confined to the caller's metrics:read scope.

ParameterTypeRequiredDescription
host_id / environment_id / client_idUUIDExactly oneThe chart's target
startRFC3339YesStart of time range
endRFC3339YesEnd of time range

Response (200): array of notes.

Update Annotation​

PUT /api/v1/annotations/:id

Replaces kind, timestamp, ends_at and text (omitting ends_at turns a range back into a point). Author only, while they can still read the note — super-admins are not exempt. Scope and author never change. 200 with the note; 403 for a non-author; 404 when the caller cannot read it. Audited as annotation_updated with before/after values.

Delete Annotation​

DELETE /api/v1/annotations/:id

Allowed for the author (while they can read the note), a moderator holding annotations:write at the note's scope (client-wide for a client note), or a super-admin.

Response (204): No content on success. Audited as annotation_deleted.

Response (403): caller can read the note but is neither its author nor a moderator. 404 when the note does not exist or is outside the caller's scope.

Chart Markers​

GET /api/v1/chart-markers?host_id=<uuid>&start=<RFC3339>&end=<RFC3339>&types=alerts,deploys

Automatic markers for one host, environment or client chart (exactly one target). Requires metrics:read on the target; each type is additionally gated — alerts needs alerts:read, deploys needs changes:read at the target's scope — and a type the caller cannot read comes back empty (200, never 403). The range may span at most 30 days.

  • alerts — alert groups whose window overlaps the range (first_fired_at <= end AND COALESCE(resolved_at, now()) >= start; a group marked resolved with no resolved_at ends at first_fired_at). Host charts show only that host's alerts. Newest 200 kept.
  • deploys — ArgoCD sync / sync_failed, GitHub release, GitLab tag. Newest 100 kept.

Client-wide deploys (no environment) appear on every chart of their client; client-wide alerts (no environment and no host) appear on the client chart only. Both are shown to readers whose grants are environment-scoped (ApplyClientWideTenantScope) — unlike the Alerts / Changes list pages.

Response (200): {"data": {"alerts": [{"id","name","severity","status","fired_at","resolved_at","host_id"}], "deploys": [{"id","at","source","event_type","summary","app","revision","url","failed"}]}}, each oldest first.


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.