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.
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:
- JWT Bearer token —
Authorization: Bearer <jwt>(user login sessions) - 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 /healthzGET /readyzGET /metrics— only whenPROXIMA_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 byX-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
| Code | HTTP Status | When |
|---|---|---|
bad_request | 400 | Invalid input, malformed JSON, validation failure |
unauthorized | 401 | Missing or invalid authentication token |
forbidden | 403 | Insufficient permissions for the requested operation |
mfa_enrollment_required | 403 | Account requires MFA enrollment before access is granted |
not_found | 404 | Requested entity does not exist |
conflict | 409 | Duplicate entry or conflicting state |
request_too_large | 413 | Request body exceeds the 1 MB limit |
internal_error | 500 | Unexpected server error |
Pagination
List endpoints support pagination via query parameters:
| Parameter | Default | Max | Description |
|---|---|---|---|
page | 1 | — | Page number (1-indexed) |
per_page | 20 | 100 | Items per page |
Example:
GET /api/v1/clients?page=2&per_page=50
Filtering and Sorting
List endpoints support filtering and sorting via query parameters:
| Parameter | Example | Description |
|---|---|---|
client_id | ?client_id=abc | Filter by client ID |
status | ?status=online | Filter by status |
sort | ?sort=hostname | Field to sort by |
order | ?order=asc | Sort 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:
| Parameter | Required | Default | Description |
|---|---|---|---|
process_name | Yes | — | Process name (for disambiguation on PID reuse) |
start | No | 1 hour ago | Start time (RFC3339) |
end | No | now | End 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:
| Parameter | Required | Default | Description |
|---|---|---|---|
start | No | 1 hour ago | Start time (RFC3339) |
end | No | now | End 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:
| Parameter | Default | Description |
|---|---|---|
start | 1 hour ago | Start time (RFC3339) |
end | now | End 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:
| Parameter | Required | Description |
|---|---|---|
host_ids | Yes | Comma-separated host IDs (UUIDs) |
metrics | Yes | Comma-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— missinghost_idsormetrics, 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:
| Parameter | Default | Description |
|---|---|---|
q | — (required) | PQL query string |
page | 1 | Page number |
per_page | 20 | Items per page (max 100) |
format | json | Response 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:
| Parameter | Required | Description |
|---|---|---|
q | Yes | PQL query string |
group_by | Yes | Field 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 }
}
Create Saved Search
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 Saved Search
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
Update Saved Search
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 Saved Search
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):
| Method | Path | Description |
|---|---|---|
| GET | /api/v1/users/:id/clients | List direct client assignments |
| POST | /api/v1/users/:id/clients | Assign user to client with role |
| PUT | /api/v1/users/:id/clients/:clientId | Change assignment role |
| DELETE | /api/v1/users/:id/clients/:clientId | Remove 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
| Method | Path | Description |
|---|---|---|
| POST | /api/v1/teams/:id/members | Add member (user_id, role_id) |
| PUT | /api/v1/teams/:id/members/:userId | Change member's role (role_id) |
| DELETE | /api/v1/teams/:id/members/:userId | Remove member |
Team Clients
| Method | Path | Description |
|---|---|---|
| POST | /api/v1/teams/:id/clients | Add client (client_id) |
| DELETE | /api/v1/teams/:id/clients/:clientId | Remove 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:
| Parameter | Type | Description |
|---|---|---|
actor_id | UUID | Filter by actor |
action | string | Filter by action (e.g., user.login, role.create) |
resource_type | string | Filter by resource type (e.g., user, team, role) |
resource_id | string | Filter by resource ID |
from | RFC3339 | Start time |
to | RFC3339 | End time |
page | int | Page number |
per_page | int | Items per page |
Authentication
Auth endpoints are documented in detail on the Authentication page. Quick reference:
Public Auth Routes (rate limited: 5/min per IP)
| Method | Path | Description |
|---|---|---|
| POST | /api/v1/auth/login | Login with email and password |
| POST | /api/v1/auth/refresh | Refresh access token (rotate refresh token) |
| POST | /api/v1/auth/mfa/verify | Verify MFA code during login |
| POST | /api/v1/auth/forgot-password | Request password reset email |
| POST | /api/v1/auth/reset-password | Reset password with token |
| POST | /api/v1/auth/accept-invite | Accept invitation and set password |
Protected Auth Routes (JWT required)
| Method | Path | Description |
|---|---|---|
| POST | /api/v1/auth/logout | Logout (revoke session) |
| GET | /api/v1/auth/me | Get current user profile + permissions |
| POST | /api/v1/auth/mfa/setup | Start MFA setup (returns TOTP secret + recovery codes) |
| POST | /api/v1/auth/mfa/confirm | Confirm MFA setup with TOTP code |
| DELETE | /api/v1/auth/mfa | Disable MFA (requires password) |
| PUT | /api/v1/auth/password | Change password |
| GET | /api/v1/auth/sessions | List active sessions |
| DELETE | /api/v1/auth/sessions/:id | Revoke a specific session |
| DELETE | /api/v1/auth/sessions | Revoke 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
}
| Field | Type | Required | Description |
|---|---|---|---|
paths | string[] | Yes | Filesystem paths to monitor (must be non-empty) |
exclude_patterns | string[] | No | Glob patterns for files to exclude |
sensitive_files | string[] | No | Glob patterns for sensitive files (tracked but content not sent) |
max_file_size | int | No | Maximum 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:
| Parameter | Type | Required | Description |
|---|---|---|---|
start | RFC3339 | Yes | Start of time range |
end | RFC3339 | Yes | End of time range |
host_id | UUID | No | Filter events for a specific host |
environment_id | UUID | No | Filter events for a specific environment |
event_type | string | No | Filter by type: deploy, file_change, agent_status |
limit | int | No | Maximum 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.
| Field | Type | Required | Description |
|---|---|---|---|
query | string | Yes | MetricsQL expression |
start | RFC3339 | Yes | Start of time range |
end | RFC3339 | Yes | End of time range |
step | string | No | Query resolution step (e.g. 60s, 5m) |
host_id | UUID | Conditional | Scope to one host. At least one of host_id / environment_id is required. |
environment_id | UUID | Conditional | Scope 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"
}
| Field | Type | Required | Description |
|---|---|---|---|
scope | host | environment | client | Yes | Immutable after create |
host_id / environment_id / client_id | UUID | The one matching scope | The annotated entity |
kind | note | maintenance | deploy | No | Default note |
timestamp | RFC3339 | Yes | Point in time, or range start |
ends_at | RFC3339 | No | Makes the note a range; must be after timestamp |
text | string | Yes | 1–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.
| Parameter | Type | Required | Description |
|---|---|---|---|
host_id / environment_id / client_id | UUID | Exactly one | The chart's target |
start | RFC3339 | Yes | Start of time range |
end | RFC3339 | Yes | End 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 noresolved_atends atfirst_fired_at). Host charts show only that host's alerts. Newest 200 kept.deploys— ArgoCDsync/sync_failed, GitHubrelease, GitLabtag. 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.