Synadia Insights
API
Insights exposes a programmatic API over NATS request/reply using NATS micro. The API starts automatically whenever a sink connection is available and provides the same query capabilities that power the web UI.
Any NATS client connected to the same server (or cluster) can issue requests to these endpoints. This enables automation, integration with external tooling, and headless operation without the web UI.
Service Info
The API registers as a NATS micro service named insights with version 0.0.1. You can discover it using the standard micro service discovery subjects ($SRV.INFO, $SRV.PING, etc.).
Subject Hierarchy
All API subjects use the $INS prefix and follow a $INS.{domain}.{action} convention:
| Domain | Subject | Description |
|---|---|---|
db | $INS.db.query | SQL query execution |
db | $INS.db.backup | Database backup |
checks | $INS.checks.list | List audit checks |
checks | $INS.checks.info | Full metadata for one check |
checks | $INS.checks.findings | Run a check and return rows |
The first token after $INS identifies the domain, the second the action. That makes subjects predictable and easy to authorize with NATS subject permissions (for example, grant $INS.db.> for all database operations).
Versioning
The API doesn't currently encode a version in the subject hierarchy. When breaking changes happen, consumers should include an Insights-Api-Version header (for example, Insights-Api-Version: 2). Requests without the header get current behavior.
Endpoints
Query: $INS.db.query
Execute arbitrary read-only SQL queries against the DuckDB database.
Request:
{
"sql": "SELECT * FROM hx.server_ident LIMIT 10",
"params": []
}
| Field | Type | Required | Description |
|---|---|---|---|
sql | string | yes | SQL query to execute. Must be read-only (SELECT, WITH, EXPLAIN) |
params | array | no | Positional parameters for parameterized queries |
Response (JSON, default):
[
{ "name": "server-1", "cluster": "us-east", "version": "2.10.0" },
{ "name": "server-2", "cluster": "us-west", "version": "2.10.0" }
]
An array of objects, one per row, keyed by column name.
Response (CSV):
Set the Accept: text/csv header to receive results as CSV.
Errors:
| Code | Condition |
|---|---|
400 | Invalid JSON, missing SQL, or non-read-only query |
500 | Internal server error |
Example using nats CLI:
nats req '$INS.db.query' '{"sql": "SELECT count(*) as n FROM hx.server_ident"}'
Checks List: $INS.checks.list
Returns all audit checks grouped by category, including configurable parameters.
Request: Empty payload.
Response:
[
{
"label": "Server Health",
"checks": [
{
"code": "SERVER_003",
"name": "High CPU Usage",
"description": "Server CPU usage exceeds threshold",
"scope": "server",
"optimization": false,
"params": [
{
"name": "cpu_percent",
"default": 90.0,
"resolved": 90.0,
"description": "CPU usage threshold percentage"
}
]
}
]
}
]
Checks Info: $INS.checks.info
Return full metadata for a single check, including its description, severity, scope, and configurable parameters with default and resolved (currently in-effect) values.
Request:
{ "code": "SERVER_003" }
| Field | Type | Required | Description |
|---|---|---|---|
code | string | yes | Check code (e.g., SERVER_003) |
Response:
{
"code": "SERVER_003",
"name": "High CPU Usage",
"description": "Server CPU usage exceeds threshold",
"scope": "server",
"severity": "warning",
"category": "Performance",
"optimization": false,
"params": [
{
"name": "cpu_percent",
"default": 90.0,
"resolved": 90.0,
"description": "CPU usage threshold percentage"
}
]
}
Errors:
| Code | Condition |
|---|---|
400 | Missing check code |
404 | Unknown check code |
500 | Internal server error |
Checks Findings: $INS.checks.findings
Execute a specific check by code and return matching results.
Request:
{
"code": "SERVER_003",
"time": {
"duration": "1h"
},
"page": 1
}
| Field | Type | Required | Description |
|---|---|---|---|
code | string | yes | Check code (e.g., SERVER_003) |
time | object | no | Time parameters with duration |
page | int | no | Page number for pagination |
Response (JSON, default):
{
"rows": [
{
"code": "SERVER_003",
"severity": "warning",
"entity": "server-1",
"entity_pk": "NABC123",
"check_name": "High CPU Usage",
"cpu_percent": 92.3
}
],
"page_info": {
"page": 1,
"total_pages": 1,
"total_rows": 2
}
}
Response (CSV): Set Accept: text/csv header.
Errors:
| Code | Condition |
|---|---|
400 | Missing check code or invalid duration |
404 | Unknown check code |
500 | Internal server error |
Backup: $INS.db.backup
Create a transactionally consistent DuckDB backup. The backup captures all tables in the hx schema, pinned to a consistent epoch to avoid partial data from concurrent indexing.
Request:
{
"start_epoch": "2024-01-01T00:00:00Z",
"end_epoch": "2024-01-02T00:00:00Z",
"upload": true,
"retain": false
}
| Field | Type | Required | Description |
|---|---|---|---|
start_epoch | string (RFC 3339) | no | Start of epoch range to include. Omit for full backup |
end_epoch | string (RFC 3339) | no | End of epoch range to include. Omit for full backup |
upload | bool | no | Upload the backup to a NATS object store (insights-backups bucket) |
retain | bool | no | When upload is true, keep the local file after uploading. Default removes it |
All fields are optional. With an empty payload {}, a full backup is created locally.
Response:
{
"filename": "insights-backup-20240115-093045.db",
"size": 52428800,
"duration": "1.234s",
"tables": 24,
"object_name": "insights-backup-20240115-093045.db",
"bucket": "insights-backups"
}
| Field | Description |
|---|---|
filename | Local filename (empty when uploaded without retain) |
size | Backup file size in bytes |
duration | Time taken to create the backup |
tables | Number of tables backed up |
object_name | Object store key (present when upload is true) |
bucket | Object store bucket name (present when upload is true) |
Only one backup can run at a time. A concurrent request returns a 409 error.
Errors:
| Code | Condition |
|---|---|
400 | Invalid request JSON |
409 | Backup already in progress |
500 | Internal server error |
The object store TTL is controlled by the backup.object-store-ttl configuration option.
Error Handling
All endpoints return errors using the NATS micro error format:
- Code. A string error code (HTTP-style:
400,404,500, and so on). - Description. A human-readable error message.
- Data. Optional additional context (JSON bytes).
Internal errors get masked with a generic 500 / "internal server error" response, so implementation details don't leak.
Content Negotiation
The db.query and checks.findings endpoints support content negotiation via the NATS message Accept header:
application/json(default). Results as a JSON array/object.text/csv. Results as CSV with a header row.