REST API & WebSocket Reference¶
Seerflow exposes a versioned HTTP API and a single WebSocket stream under the dashboard process. The dashboard SPA and external integrations use the same endpoints — there is no separate private API.
Base URL and Version¶
All routes are prefixed with /api/v1. The dashboard listens on
dashboard_port (default 8080). Health probes bind to
health_bind_address (default 127.0.0.1).
http://<host>:<dashboard_port>/api/v1/...
ws://<host>:<dashboard_port>/api/v1/ws
Conventions¶
- Content types: request bodies are JSON; responses are JSON unless noted.
- Pagination: list endpoints accept
page(1-based) andlimit. The response includes ametaenvelope:{total, page, limit}. - Time windows:
since/untilaccept ISO-8601 timestamps; some endpoints additionally accept relative ranges (range=1h|6h|24h|7d) paired withresolution=1m|5m|15m|1h. - Errors: standard HTTP status codes.
404= resource not found,503= subsystem not ready (LLM disabled, config not loaded, UEBA warming up),502= upstream LLM failure,409= idempotency collision,422= validation failure,507= storage capacity exceeded.
Rate Limiting¶
Configured via the top-level api_* keys
(Config Reference → API). Three families:
| Family | Default | Endpoints |
|---|---|---|
| List | 60/minute |
/events, /alerts, /sigma/rules, … |
| Detail | 300/minute |
/alerts/{id}, /entities/{uuid}/..., … |
| Coverage | 10/minute |
/attack/coverage |
Set api_rate_limit_redis_url to scale limits across multiple Seerflow
instances. Set api_trust_proxy_headers: true only when behind a
trusted reverse proxy.
CORS¶
api_allowed_origins enumerates browser origins permitted to call the
API. Empty = same-origin only.
Alerts¶
| Method | Path | Purpose |
|---|---|---|
GET |
/alerts |
Paginated alert search. |
GET |
/alerts/{alert_id} |
Single alert. |
POST |
/alerts/{alert_id}/feedback |
Submit TP/FP feedback. |
GET |
/alerts/{alert_id}/feedback |
Newest-first feedback audit log. |
POST |
/alerts/{alert_id}/explain |
Generate or fetch a cached LLM explanation. |
GET |
/alerts/{alert_id}/explanation |
Return cached explanation only (never invokes the LLM). |
GET /alerts¶
Query parameters:
| Param | Type | Description |
|---|---|---|
since, until |
ISO-8601 | Window. |
type |
ml,sigma,correlation,ueba,ioc |
Alert type filter. |
severity |
int | Minimum severity. |
entity |
string | Entity UUID5 or value (IP, hostname, user). |
tactic |
string | ATT&CK tactic name. |
technique |
string | ATT&CK technique ID (prefix-matched). |
page, limit |
int | Pagination (max limit = 1000). |
POST /alerts/{alert_id}/feedback¶
Body:
{
"feedback": "tp",
"note": "Confirmed lateral movement",
"origin": "console"
}
Returns 204 No Content. 404 if alert missing.
POST /alerts/{alert_id}/explain¶
Generates a markdown explanation via the configured LLM backend.
Subsequent calls return the cached entry until TTL expiry
(llm.explanation_*).
503if LLM disabled / backend not ready.502on upstream LLM failure.404if alert missing.
Events¶
| Method | Path | Purpose |
|---|---|---|
GET |
/events |
Paginated event search. |
Query parameters: since, until, source, severity, template_id,
entity, q (free-text substring search), page, limit (max
1000).
Entities¶
| Method | Path | Purpose |
|---|---|---|
GET |
/entities/search |
Entity search by free-text value or UUID. |
GET |
/entities/{entity_uuid}/timeline |
Cross-source event timeline + related entities. |
GET |
/entities/{entity_uuid}/risk-history |
Bucketed risk-score series. |
GET |
/entities/{entity_uuid}/baseline |
Current UEBA baseline. |
GET /entities/search¶
| Param | Type | Description |
|---|---|---|
q |
string (1–256 chars) | Free-text or UUID. |
Returns IP, user, host, and domain UUIDs ranked by relevance.
GET /entities/{entity_uuid}/timeline¶
| Param | Type | Description |
|---|---|---|
start_ns, end_ns |
int (ns) | Time bounds. |
source_type |
string | Filter by source. |
severity_min |
int | Minimum severity. |
limit |
int | Max events (cap 10_000). |
GET /entities/{entity_uuid}/baseline¶
Returns the UEBA baseline for the entity (top-K sources, top-K templates, hour-of-day distribution, volume EMA).
503if UEBA disabled.404if entity unknown.404withstatus: warming_upwhile inside the warmup window.
Anomaly¶
| Method | Path | Purpose |
|---|---|---|
GET |
/anomaly/timeline |
Bucketed anomaly-score series with alert counts. |
| Param | Type | Description |
|---|---|---|
range |
1h,6h,24h,7d |
Window length. |
resolution |
1m,5m,15m,1h |
Bucket size. |
source |
string | Filter by source. |
ATT&CK Coverage¶
| Method | Path | Purpose |
|---|---|---|
GET |
/attack/coverage |
ATT&CK matrix with per-technique fire counts. |
| Param | Type | Description |
|---|---|---|
since, until |
ISO-8601 | Window. Default: last 30 days. |
Rate-limited under api_coverage_rate_limit (default 10/minute).
Hunt¶
| Method | Path | Purpose |
|---|---|---|
POST |
/hunt |
Natural-language threat hunt via LLM. |
Body:
{ "query": "failed logins by service accounts in the last hour" }
503if LLM not ready.502on upstream LLM failure.400on invalid or over-budget query (seellm.hunt_max_query_chars).
Falls back to a deterministic entity-timeline lookup when the query matches an entity (IP, hostname, UUID5).
Sigma Rules¶
All paths under /sigma.
| Method | Path | Purpose |
|---|---|---|
GET |
/sigma/rules |
Paginated rule list with 24 h fire counts. |
GET |
/sigma/rules/{rule_id} |
Single rule incl. YAML. |
GET |
/sigma/rules/{rule_id}/timeline |
24 h hourly firing trend. |
PATCH |
/sigma/rules/{rule_id} |
Toggle enabled. |
POST |
/sigma/rules |
Upload custom rule (validate + persist). |
GET |
/sigma/rule-suggestions |
List TP-feedback patterns eligible for LLM drafting. |
POST |
/sigma/rule-suggestions/{pattern_key} |
Draft a Sigma YAML for the pattern. |
DELETE |
/sigma/rule-suggestions/{pattern_key} |
Invalidate cached suggestion. |
POST /sigma/rules¶
Body:
{ "yaml": "<sigma yaml>", "dry_run": false }
201on success.409on rule-ID collision.422on validation failure (returns line/column).507ifdetection.sigma_custom_upload_diris full.
POST /sigma/rule-suggestions/{pattern_key}¶
pattern_key has the form alert_type:rule_name:entity_type. Returns
drafted YAML + supporting evidence (TP feedbacks, contributing events).
503if LLM disabled / backend not ready.502on upstream LLM failure.404if the pattern no longer satisfiesrule_suggestion_min_tp.
System¶
| Method | Path | Purpose |
|---|---|---|
GET |
/health |
Service health and component readiness. |
GET |
/stats |
Pipeline counters and live metrics. |
GET |
/config |
Running config with secrets redacted. |
GET /health returns HTTP 503 when any component is degraded; the
body includes per-component detail and detection / feedback extras.
GET /config masks every credential-bearing field (postgresql_url,
falkordb_url, api_rate_limit_redis_url, channel tokens, …).
WebSocket — /api/v1/ws¶
Real-time event and alert stream consumed by the dashboard's live feed.
Connect¶
GET /api/v1/ws
Upgrade: websocket
Origin: https://dashboard.example.com
Origin is validated against ws_allowed_origins (defaults to localhost
derived from dashboard_port) — required for CSWSH defence.
ws_max_connections caps concurrent clients (default 20).
Server → client frames¶
type |
Description |
|---|---|
event |
Single newly-ingested event. |
alert |
Single newly-generated alert. |
batch |
Up to ws_batch_max_events events, flushed on the ws_tick_interval_s tick. |
alert_batch |
Coalesced alerts on the same flush. |
status |
Periodic backpressure / health frame on ws_status_interval_s. |
Client → server frames¶
{
"type": "filter",
"min_severity": 3,
"source": ["nginx", "syslog"],
"entity": "550e8400-e29b-41d4-a716-446655440000"
}
Filter updates are throttled to one every ws_filter_min_interval_ms.
Backpressure¶
Per-client buffer length: ws_queue_maxlen. When a client cannot keep
up, Seerflow drops oldest frames first and emits a status frame with
dropped: N.
Authentication¶
The default deployment binds the dashboard to 127.0.0.1 and assumes
network-level access control (reverse proxy, VPN, or SSH tunnel).
Authentication / RBAC integration is planned post-1.0 and is not yet
documented here.
For now, expose the dashboard publicly only via a reverse proxy that terminates TLS and enforces authentication.