Skip to content

API Reference

All API endpoints are under /api/v1. Responses are JSON.

Authentication

Most endpoints require authentication via session cookie or API key.

Session (browser)

POST /api/v1/auth/login
Content-Type: application/json

{"username": "admin", "password": "changeme"}

Sets an HttpOnly session cookie valid for 24 hours.

POST /api/v1/auth/logout

API Key

Include the key in one of two ways:

Authorization: Bearer strix_<key>
X-API-Key: strix_<key>

API keys are scoped to a specific fractal. See API Keys for key management.


Log Ingestion

Ingestion endpoints require token authentication and are rate-limited. Logs are accepted into a buffered queue and inserted into ClickHouse asynchronously by a worker pool.

Strix native format

POST /api/v1/ingest
Content-Type: application/json

Accepts three formats:

JSON array:

[
  {"event_id": 1, "image": "C:\\Windows\\System32\\cmd.exe"},
  {"event_id": 4624, "user": "SYSTEM"}
]

Single object:

{"message": "user login", "user": "admin", "source_ip": "10.0.0.1"}

NDJSON (newline-delimited):

{"event_id": 1, "image": "powershell.exe"}
{"event_id": 4624, "user": "admin"}

Fractal routing

Each ingest token is scoped to a single fractal. Logs are routed to the fractal associated with the token.

Elasticsearch bulk format (Velociraptor compatible)

POST /_bulk
PUT /_bulk

Accepts standard Elasticsearch NDJSON bulk format:

{"index": {"_index": "logs"}}
{"event_id": 1, "image": "powershell.exe"}
{"create": {}}
{"event_id": 4624, "user": "admin"}

Response follows the Elasticsearch bulk response schema.

Response codes

Status Meaning
200 Logs accepted into ingestion queue
400 Invalid JSON or no valid logs found
413 Request body exceeds size limit (default 200MB)
429 Rate limit exceeded or ingestion queue full. Retry with backoff

Tuning

Environment variable Default Description
STRIX_INGEST_WORKERS 4 Worker goroutines inserting into ClickHouse
STRIX_INGEST_QUEUE_SIZE 500 Pending batch slots (each slot = one request's logs)
STRIX_MAX_BODY_SIZE 209715200 Max request body in bytes (200MB)
STRIX_INGEST_RATE_LIMIT 10000 Sustained requests/second
STRIX_INGEST_RATE_BURST 20000 Burst capacity

Querying

POST /api/v1/query
Content-Type: application/json
Authorization: Bearer strix_<key>

Request body:

{
  "query": "event_id=1 | groupBy(image) | count()",
  "fractal_id": "uuid-of-fractal",
  "start": "2026-01-01T00:00:00Z",
  "end": "2026-01-02T00:00:00Z"
}
Field Type Description
query string Quandrix query (required)
fractal_id string Target fractal UUID (uses session default if omitted)
start string RFC3339 start time (defaults to 24 hours ago)
end string RFC3339 end time (defaults to now)

Response:

{
  "success": true,
  "results": [{"image": "powershell.exe", "count": 42}],
  "count": 1,
  "query": "event_id=1 | groupBy(image) | count()",
  "sql": "SELECT ...",
  "execution_ms": 12,
  "field_order": ["image", "count"],
  "is_aggregated": true,
  "chart_type": "",
  "limit_hit": ""
}
Field Description
results Array of result rows
count Number of rows returned
sql Generated SQL (Quandrix queries only)
field_order Column display order
is_aggregated True if query used aggregation
chart_type "piechart", "barchart", "graph", or empty
limit_hit "search", "bloom", "truncated", or empty

Queries time out after 60 seconds by default (configurable in Settings). Responses exceeding ~50MB are truncated to 1000 rows.


Recent Logs

GET /api/v1/logs/recent

Returns up to 50 recent logs for the selected fractal. Useful for exploring available fields.


Comments

POST   /api/v1/comments
GET    /api/v1/comments/{id}
PUT    /api/v1/comments/{id}
DELETE /api/v1/comments/{id}

GET    /api/v1/logs/{log_id}/comments
DELETE /api/v1/logs/{log_id}/comments

GET    /api/v1/logs/commented

Create comment:

{
  "log_id": "uuid",
  "content": "Confirmed malicious - escalating to IR team",
  "fractal_id": "uuid"
}

Fractals

GET    /api/v1/fractals
POST   /api/v1/fractals           (admin)
GET    /api/v1/fractals/{id}
PUT    /api/v1/fractals/{id}      (admin)
DELETE /api/v1/fractals/{id}      (admin)
POST   /api/v1/fractals/{id}/select
GET    /api/v1/fractals/{id}/stats
POST   /api/v1/fractals/stats/refresh  (admin)

Alerts

GET    /api/v1/alerts
POST   /api/v1/alerts
GET    /api/v1/alerts/{id}
PUT    /api/v1/alerts/{id}
DELETE /api/v1/alerts/{id}
POST   /api/v1/alerts/import
GET    /api/v1/alerts/{id}/executions

Webhooks

GET    /api/v1/webhooks            (admin)
POST   /api/v1/webhooks            (admin)
GET    /api/v1/webhooks/{id}       (admin)
PUT    /api/v1/webhooks/{id}       (admin)
DELETE /api/v1/webhooks/{id}       (admin)
POST   /api/v1/webhooks/{id}/test  (admin)

Webhook configuration

Field Type Description
name string Unique webhook name
url string Destination URL
method string HTTP method (default: POST)
headers object Custom HTTP headers
auth_type string none, bearer, or basic
auth_config object Auth details (token for bearer; username/password for basic)
timeout_seconds int Request timeout (default: 30)
retry_count int Retry attempts with exponential backoff (default: 3)
include_alert_link bool Include a UI link to the alert results (default: true)

Alert webhook payload

When an alert fires, each configured webhook receives:

{
  "alert_name": "Security Alert for 10.0.0.5",
  "original_name": "Security Alert for {{src_ip}}",
  "alert_id": "uuid",
  "description": "Detects suspicious login patterns",
  "labels": ["sigma:high", "product:windows"],
  "triggered_at": "2026-03-01T12:34:56Z",
  "query_string": "event_id=4625 | count() > 10",
  "match_count": 15,
  "alert_link": "https://strix.example.com/?q=...",
  "results": [
    {"src_ip": "10.0.0.5", "user": "admin", "event_id": "4625"}
  ]
}
Field Description
alert_name Resolved name (field templates like {{src_ip}} are replaced with values from the first result)
original_name Only present if the name contained templates
results All matching log records from the evaluation window
match_count Number of results
alert_link Shareable UI link (only if include_alert_link is enabled and STRIX_BASE_URL is set)

Webhooks block requests to loopback and private network addresses.


Notebooks

GET    /api/v1/notebooks
POST   /api/v1/notebooks
GET    /api/v1/notebooks/{id}
PUT    /api/v1/notebooks/{id}
DELETE /api/v1/notebooks/{id}

POST   /api/v1/notebooks/{id}/sections
PUT    /api/v1/notebooks/{id}/sections/{section_id}
DELETE /api/v1/notebooks/{id}/sections/{section_id}
POST   /api/v1/notebooks/{id}/sections/{section_id}/execute
POST   /api/v1/notebooks/{id}/sections/reorder

Dashboards

GET    /api/v1/dashboards
POST   /api/v1/dashboards
GET    /api/v1/dashboards/{id}
PUT    /api/v1/dashboards/{id}
DELETE /api/v1/dashboards/{id}

POST   /api/v1/dashboards/{id}/widgets
PUT    /api/v1/dashboards/{id}/widgets/{widget_id}
DELETE /api/v1/dashboards/{id}/widgets/{widget_id}

Health

GET /api/v1/health

Returns {"status": "healthy"}. No authentication required.


Status

GET /api/v1/status

Returns ClickHouse and PostgreSQL status.