Daily Use

REST API

obserae exposes an HTTP API you can drive from scripts, supervision systems and CI. It is the same server that renders the Web GUI: the browser pages return HTML, while every path under /api/… returns JSON and every path under /ws/… is a WebSocket. This page is the reference for the JSON API — how to authenticate, what each endpoint does, and copy-paste curl examples.

  • Base URL — the web server binds 127.0.0.1:8080 by default (web.bind in configuration). All examples below use http://127.0.0.1:8080; substitute your host and port.
  • Content type — send Content-Type: application/json on requests with a body; responses are always application/json.
  • Read vs. write — read endpoints (GET) run on a dedicated reader pool and never block ingestion; mutations (POST/PATCH/DELETE) serialise on the writer.
  • Machine-readable spec — a full OpenAPI 3.1 description of every endpoint ships alongside this page as openapi.yaml. Import it into Postman/Insomnia, generate a client, or render it with Swagger UI / Redoc.

Authentication

Every /api/… endpoint requires an API token, sent as a Bearer credential:

Authorization: Bearer obs_…

The only exceptions are the two public probes documented below (GET /healthz and GET /api/protocols).

Minting a token

A token is minted for a user and inherits that user’s permissions. The secret is shown once at creation — copy it then; obserae only stores a hash.

  • In the GUIIdentity & Access → Users → + New token. See Web GUI › API tokens.

  • From the CLI — over the admin socket (the secret is printed once):

    obserae-cli user token-add --name ci --user alice
    obserae-cli user token-ls
    obserae-cli user token-rm <TOKEN_ID>   # revoke
    

Give the token’s owner the least privilege it needs. A supervision system that only polls /api/status, for example, should own a token whose user is in the built-in monitoring group (just monitoring:read) and nothing else.

No CSRF, no MFA for tokens

Bearer-token clients are exempt from CSRF and from the MFA enrollment gate — a script never needs a CSRF header or a second factor. (CSRF only applies to the browser, which authenticates with a session cookie.) See authentication for how humans sign in.

Permissions

Authorization is enforced per route. If the token’s user lacks the required permission, the call fails 403 (see Errors). The catalogue:

PermissionGrants
cartography:read / cartography:writeRead / edit the network map (hosts, networks, groups, services)
rules:read / rules:writeRead / edit flow-matrix rules
alerting:read / alerting:writeRead / edit detection & anomaly rules and rule sets
sessions:readRead sessions, run read-only NFQL helpers
nfql:executeExecute NFQL queries and manage saved queries
alerts:read / alerts:ackRead / acknowledge & delete detection alerts
outputs:manageManage alert outputs (webhooks, syslog, …)
sources:manageManage enrichment sources and flow exporters
devices:manageManage OPNsense devices
config:export / config:importExport / import the YAML config bundle
users:manageManage users, groups and tokens
auth:manageManage LDAP/OIDC and login-security settings
auditlog:readRead and verify the audit log
monitoring:readRead the GET /api/status health snapshot
system:manageStorage / retention / backup / master-key operations

Built-in groups bundle these: admin (everything), analyst (read + query + rule authoring), auditor (read-only, incl. auditlog:read), and monitoring (only monitoring:read). Create a custom group to grant an exact subset.

Errors

Machine paths (/api/…, /ws/…) always answer failures with a JSON body:

StatusBodyMeaning
401 Unauthorized{"error":"unauthenticated"}Missing, unknown, revoked or expired token
403 Forbidden{"error":"forbidden"}Token’s user lacks the required permission
423 Locked{"error":"…","holder":"<user>"}A cartography mutation while another admin holds the edit lock
400 Bad Request{"error":"…"}Malformed request (missing field, bad JSON)

NFQL compile errors are not HTTP errors. POST /api/query returns 200 OK with an error field populated (see below) so a positioned compile error and a result share one response shape.


Health & status

MethodPathPermissionPurpose
GET/healthzpublicPlain-text liveness probe — returns ok
GET/api/statusmonitoring:readFull machine-facing daemon snapshot (JSON)
GET/api/protocolspublicIANA protocol number → name table (JSON)

GET /api/status returns byte-for-byte the same payload as obserae-cli status — one shared snapshot for both surfaces. It is the authenticated counterpart to the public /healthz probe.

curl -H "Authorization: Bearer obs_…" http://127.0.0.1:8080/api/status
{
  "version": "0.27.0",
  "commit": "7a8e5e4",
  "started_at": "2026-07-07T08:00:00Z",
  "uptime_seconds": 3600,
  "timestamp": "2026-07-07T09:00:00Z",
  "data_version": 10,
  "data_version_target": 10,
  "flows":       { "total": 1284003, "per_sec": 412.5 },
  "cartography": { "networks": 12, "hosts": 340, "services": 28, "groups": 9 },
  "sessions":    { "active": 1204, "half_open": 33, "closed": 98120,
                   "open_live": 1237, "open_max": 200000, "fill_pct": 0.6,
                   "pressure": "ok", "evicted": 0,
                   "dropped_closes": 0, "dropped_emits": 0 },
  "rules":       { "total": 42, "expansions": 1180, "alert_rules_active": 7 },
  "coverage":    { "rate_1h": 0.98, "violations_closed_1h": 3 },
  "alerts":      { "total": 15, "by_status": { "open": 4, "acknowledged": 11 },
                   "by_severity": { "high": 2, "medium": 9, "low": 4 },
                   "last_1h": 1, "last_24h": 6 },
  "ingestion":   { "files_written": 8123, "records_ingested": 1284003,
                   "last_flush_at": "2026-07-07T08:59:57Z",
                   "avg_flush_ms": 41.2, "max_flush_ms": 190 },
  "storage":     { "db_path": "data/obserae.duckdb", "db_size_bytes": 12288,
                   "disk_free_bytes": 51230000000, "disk_total_bytes": 100000000000 },
  "runtime":     { "heap_inuse_bytes": 84934656, "goroutines": 61,
                   "udp_drops": 0, "matcher_cursor_lag_seconds": 1 },
  "audit":       { "integrity": "verified", "breaks": 0 },
  "live_metrics_available": true
}

When the web GUI is disabled (web.enabled: false) the producer-derived blocks (per_sec, coverage, runtime, ingestion timing, audit verdict) stay at zero and live_metrics_available is false; the always-available counts are still filled.


NFQL query execution

Run NFQL queries — the same engine the Investigation page uses. Compiled once, executed on the read-only pool with a row cap and timeout.

MethodPathPermissionPurpose
POST/api/querynfql:executeExecute a query, return columns + rows
GET/api/query/columnssessions:readCompile only — return output columns & types (?query=…)
GET/api/query/schemasessions:readTables, columns, keywords and constants with descriptions
GET/api/query/cookbooksessions:readBuilt-in example queries
GET/api/query/carto-refssessions:readCartography names usable in predicates
GET/api/query/resolve-ipsessions:readResolve an IP to its host/interface/network (?ip=…)

Executing a query

POST /api/query with a JSON body:

FieldTypeNotes
sourcestringThe NFQL pipeline (required)
argsarrayPositional ? arguments; integers are bound as integers
as_ofstringOptional RFC3339 instant; pins the “now” of every relative window (the investigate at fire time path). Empty = live now
curl -X POST http://127.0.0.1:8080/api/query \
  -H "Authorization: Bearer obs_…" \
  -H "Content-Type: application/json" \
  -d '{
    "source": "FROM sessions | LAST 3600 | STATS COUNT(*) AS n BY server_port | SORT n DESC | HEAD 5",
    "args": []
  }'

Response (a result or a compile error, always 200 OK):

{
  "columns": [
    { "name": "server_port", "type": "INTEGER" },
    { "name": "n",           "type": "BIGINT"  }
  ],
  "rows": [
    [443, 8123],
    [53, 4102],
    [22, 512]
  ],
  "compile_ms": 3,
  "exec_ms": 41,
  "row_count": 3,
  "truncated": false
}

On a bad query the same shape carries the message instead:

{ "columns": null, "rows": null, "error": "sem: 1:18: unknown column \"prtocol\"" }

truncated: true means the row cap was hit — narrow the query with HEAD or a tighter window. Saved queries are managed under /api/queries (list/search with sessions:read; create/update/delete with nfql:execute).


Endpoint reference by domain

All paths below are under the base URL and require the listed permission. GET returns JSON; mutations take a JSON body and return the updated resource or a status envelope. {…} marks a path parameter.

Cartography — the network map

cartography:read for GET, cartography:write for mutations. Mutations also require the edit lock; a non-holder gets 423 Locked.

MethodPathPurpose
GET/api/carto/graphFull graph (nodes, edges, groups)
GET/api/carto/layout | /color | /icon | /os-iconLayout & styling data
GET/api/carto/searchSearch hosts/networks/groups
POST/api/carto/networks | /groups | /hosts | /interfaces | /servicesCreate
PATCH DELETE/api/carto/{kind}/{name}Update / delete
POST/api/carto/hosts/{name}/cloneClone a host
POST/api/carto/auto-layoutRecompute layout
GET POST/api/carto/lock, /lock/heartbeat, /lock/releaseAcquire/renew/release the single-editor lock

Flow-matrix rules

rules:read / rules:write. See rules.

MethodPathPurpose
GET/api/rules, /api/rules/{name}List / read
POST/api/rulesCreate
PATCH DELETE/api/rules/{name}Update / delete
POST/api/rules/{name}/enable | /disableToggle
POST/api/rules/bulkBulk apply
POST/api/rules/preview, /api/rules/find-coveringDry-run helpers

Detection (alerting) rules & rule sets

alerting:read / alerting:write. See alerting and rulesets.

MethodPathPurpose
GET POST/api/alerting-rulesList / create alert rules
GET PATCH DELETE/api/alerting-rules/{id}Read / update / delete
POST/api/alerting-rules/{id}/toggle | /duplicateEnable-disable / clone
GET/api/alerting-rules/{id}/runsRecent evaluations
GET/api/rulesets, /api/rulesets/shipped, /api/rulesets/{id}Browse rule packs
POST/api/rulesets/validate | /importValidate / import a pack
POST DELETE/api/rulesets/{id}/toggle, /api/rulesets/{id}Toggle / delete

Anomaly detection

alerting:read / alerting:write. See anomaly detection.

MethodPathPurpose
GET/api/anomaly/overview, /api/anomaly/rulesOverview & rule list
GET/api/anomaly/rules/{id}/baselines, .../baselines/{keyhash}Learned baselines
GET/api/anomaly/rules/{id}/baselines/{keyhash}/series | /heatmapTime series
GET/api/anomaly/rules/{id}/firesRecent fires
POST/api/anomaly/rules/{id}/reset-baselineReset the baseline

Detection alerts

alerts:read for GET, alerts:ack for mutations.

MethodPathPurpose
GET/api/alertsList alerts (filters via query params)
POST/api/alerts/{id}/statusAcknowledge / change status
POST/api/alerts/deleteBulk delete
DELETE/api/alerts/{id}Delete one
# List, then acknowledge one alert
curl -H "Authorization: Bearer obs_…" http://127.0.0.1:8080/api/alerts
curl -X POST http://127.0.0.1:8080/api/alerts/42/status \
  -H "Authorization: Bearer obs_…" -H "Content-Type: application/json" \
  -d '{"status":"acknowledged"}'

Sessions

MethodPathPermissionPurpose
GET/api/sessions/riverviewsessions:readRiver-view session aggregation (JSON)

Enrichment & exporters

sources:manage. See enrichment and exporters.

MethodPathPurpose
GET PATCH/api/enrichmentRead / update enrichment settings
GET/api/enrichment/sourcesList enrichment sources
PATCH/api/enrichment/sources/{name}Update a source
POST/api/enrichment/sources/{name}/refreshRefresh now
GET/api/exportersList flow exporters
PATCH DELETE/api/exporters/{ip}Update / forget an exporter
POST/api/exporters/rescanRescan for exporters

Devices (OPNsense)

devices:manage. See sources.

MethodPathPurpose
GET POST/api/devicesList / add a device
GET PATCH DELETE/api/devices/{id}Read / update / remove
POST/api/devices/{id}/refreshPoll the device now

Outputs

outputs:manage. See outputs.

MethodPathPurpose
GET POST/api/outputsList / create an output
GET PATCH DELETE/api/outputs/{id}Read / update / delete
POST/api/outputs/{id}/toggle | /testEnable-disable / send a test event
GET/api/outputs/{id}/deliveriesRecent delivery attempts

Lifecycle — storage, retention, backup

system:manage. See lifecycle.

MethodPathPurpose
GET/api/lifecycle/storageStorage usage
GET PATCH/api/lifecycle/retentionRead / set retention policy
POST/api/lifecycle/retention/runRun retention now
GET PATCH/api/lifecycle/backupRead / set backup schedule
POST/api/lifecycle/backup/run, /plan, /restoreRun / plan / restore
GET/api/lifecycle/backup/timeline, /next-actionSchedule state

Configuration I/O

See CLI › config for the equivalent bundle operations.

MethodPathPermissionPurpose
GET/api/config-io/exportconfig:exportExport the full YAML config bundle
POST/api/config-io/validate | /importconfig:importValidate / import a bundle
GET POST/api/config-io/masterkey/export | /importsystem:manageMaster-key material

Audit log

auditlog:read. See verify for the tamper-evidence scheme.

MethodPathPurpose
GET/api/auditlogQuery the journal (filters via query params)
GET/api/auditlog/histogram, /facetsAggregates for the UI
GET/api/auditlog/verifyVerify the hash chain & seals
GET/api/auditlog/exportExport matching entries

Identity & access administration

users:manage unless noted; LDAP/OIDC and login security need auth:manage. The account self-service endpoints require only an authenticated token (any user managing their own MFA/password).

MethodPathPermissionPurpose
POST/api/usersusers:manageCreate a user
PATCH DELETE/api/users/{username}users:manageUpdate / delete
POST/api/users/{username}/password | /mfa/resetusers:manageReset password / MFA
POST/api/groups, PATCH/DELETE /api/groups/{name}users:manageManage groups
POST/api/tokens, /api/tokens/{id}/revoke, DELETE /api/tokens/{id}users:manageManage API tokens
GET PUT/api/ldap/config, POST /api/ldap/test-connectionauth:manageLDAP config
GET PUT/api/oidc/config, POST /api/oidc/test-connectionauth:manageOIDC config
GET PUT/api/auth/login-security, PUT /api/auth/mfa-policyusers:manageLogin-security & MFA policy
POST/api/account/mfa/begin | /activate | /disable, /api/account/passwordany authenticatedSelf-service MFA & password

WebSockets

Real-time streams for the GUI, also usable by a client that keeps a socket open. Connect with the same Bearer token; an unauthenticated handshake gets a 401.

PathPermissionPushes
/ws/healthany authenticatedHealth / cockpit snapshots
/ws/alertsalerts:readalert.fired notifications
/ws/cartocartography:readcarto.changed broadcasts when the map is edited

See also

  • Web GUI — where to mint tokens and manage permissions.
  • Authentication — how humans sign in (local, LDAP, OIDC).
  • NFQL — the query language used by POST /api/query.
  • CLI — the admin socket, which mirrors many of these operations.

Browse the interactive API reference. Every endpoint, schema and example rendered from the OpenAPI spec: /docs/api-reference/.