API Reference
Complete REST API reference for integrating with RhythmX. All endpoints are served from https://<RHYTHMX_HOST>/api/.
Authentication
RhythmX uses two authentication methods depending on the endpoint type:
| Method | Usage | Header |
|---|---|---|
| JWT Token | Dashboard & admin endpoints | Authorization: Bearer <token> |
| API Key | External feed endpoints | X-API-Key: smk_<key> |
| None | Health endpoints, webhooks | No authentication required |
Obtaining a JWT Token
POST /api/login
Content-Type: application/json
{
"username": "analyst@domain.local",
"password": "password"
}
Response:
{
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...",
"user": {
"username": "analyst@domain.local",
"role": "admin"
}
}
Use the access_token in subsequent requests:
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...
Incidents API
Manage security incidents — create, update status, assign analysts, link external tickets.
List Incidents
GET /api/incidents
Query Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
status |
string | — | Filter: OPEN, IN_PROGRESS, CLOSED, FALSE_POSITIVE |
entity |
string | — | Filter by entity name |
analyst |
string | — | Filter by assigned analyst |
search |
string | — | Free-text search on actor key or entity name |
sort_by |
string | status-priority | Sort: last_alarm_time, created_at, risk_score |
since |
string | — | ISO 8601 timestamp — only incidents created after this time |
updated_since |
string | — | ISO 8601 timestamp — only incidents updated after this time |
limit |
integer | 100 | Results per page (max 500) |
offset |
integer | 0 | Pagination offset |
Example — SOAR polling for new incidents:
GET /api/incidents?since=2026-03-13T00:00:00Z&status=OPEN&limit=50
Example — fetch recently updated incidents:
GET /api/incidents?updated_since=2026-03-13T03:00:00Z
Response:
{
"success": true,
"incidents": [
{
"incident_id": "INC-7601d999",
"group_key": "jsmith",
"actor_type": "user",
"entity_name": "Primary Site",
"status": "OPEN",
"assigned_analyst": null,
"first_alarm_time": "2026-03-12T18:26:28",
"last_alarm_time": "2026-03-13T03:49:20",
"created_at": "2026-03-12T18:26:28",
"updated_at": "2026-03-13T03:49:20",
"closed_at": null,
"external_ticket_id": null,
"external_ticket_url": null,
"external_system": null,
"investigation_notes": null,
"resolution_notes": null,
"risk_score": 82,
"risk_level": "critical",
"alarm_count": 156,
"alarm_count_24h": 23,
"alarm_count_7d": 89,
"sigma_alert_count": 47,
"threat_case_count": 3
}
],
"total": 12,
"limit": 50,
"offset": 0,
"has_more": false
}
Get Incident Detail
GET /api/incidents/{incident_id}
Response:
{
"success": true,
"incident": {
"incident_id": "INC-7601d999",
"group_key": "jsmith",
"actor_type": "user",
"entity_name": "Primary Site",
"status": "OPEN",
"assigned_analyst": "analyst1",
"investigation_notes": "Investigating lateral movement pattern",
"resolution_notes": null,
"external_ticket_id": "SEC-1234",
"external_ticket_url": "https://jira.example.com/browse/SEC-1234",
"external_system": "jira",
"first_alarm_time": "2026-03-12T18:26:28",
"last_alarm_time": "2026-03-13T03:49:20",
"created_at": "2026-03-12T18:26:28",
"updated_at": "2026-03-13T03:49:20",
"closed_at": null
}
}
Update Incident (General)
PATCH /api/incidents/{incident_id}
Content-Type: application/json
{
"status": "IN_PROGRESS",
"assigned_analyst": "analyst1",
"investigation_notes": "Reviewing related threat cases",
"resolution_notes": null,
"external_ticket_id": "SEC-1234",
"external_ticket_url": "https://jira.example.com/browse/SEC-1234",
"external_system": "jira"
}
All fields are optional — include only the fields you want to update.
Update Incident Status
PATCH /api/incidents/{incident_id}/status
Content-Type: application/json
{
"status": "CLOSED",
"notes": "Resolved — confirmed benign admin activity"
}
Valid statuses: OPEN, IN_PROGRESS, CLOSED, FALSE_POSITIVE
Assign Incident
PATCH /api/incidents/{incident_id}/assign
Content-Type: application/json
{
"analyst": "john.doe"
}
Update Incident Notes
PATCH /api/incidents/{incident_id}/notes
Content-Type: application/json
{
"investigation_notes": "Checked LDAP logs — confirmed password spray from external IP",
"resolution_notes": "Account locked, IP blocked at firewall"
}
Link External Ticket
PATCH /api/incidents/{incident_id}/external-ticket
Content-Type: application/json
{
"ticket_id": "SEC-1234",
"ticket_url": "https://jira.example.com/browse/SEC-1234",
"system": "jira"
}
Bulk Status Update
PATCH /api/incidents/bulk-status
Content-Type: application/json
{
"incident_ids": ["INC-7601d999", "INC-3e08d543", "INC-8b5d98b0"],
"status": "CLOSED"
}
Promote Actor to Incident
Manually create an incident for an actor that doesn't meet auto-creation thresholds. Idempotent — returns existing incident if already created.
POST /api/incidents/promote
Content-Type: application/json
{
"group_key": "john.doe",
"actor_type": "user",
"entity_name": "Primary Site",
"first_alarm": "2026-02-25T10:00:00",
"last_alarm": "2026-02-26T14:30:00"
}
Response (201 Created):
{
"success": true,
"incident": { "incident_id": "INC-a1b2c3d4", "..." : "..." },
"created": true,
"message": "Incident created for john.doe"
}
Get Incident History
Returns the audit trail of all changes (status changes, assignments, notes).
GET /api/incidents/{incident_id}/history
Response:
{
"success": true,
"history": [
{
"id": 1,
"incident_id": "INC-7601d999",
"field_changed": "status",
"old_value": "OPEN",
"new_value": "IN_PROGRESS",
"changed_by": "analyst1",
"changed_at": "2026-03-13T04:15:00",
"notes": "Starting investigation"
}
]
}
Incident Statistics
GET /api/incidents/stats?entity=Primary%20Site
Response:
{
"success": true,
"stats": {
"total": 45,
"OPEN": 12,
"IN_PROGRESS": 8,
"CLOSED": 20,
"FALSE_POSITIVE": 5
}
}
Cases API
Manage threat cases from the correlation engine (23 use cases).
List Active Cases
GET /api/cases/active?limit=50&entity_name=Primary%20Site
Query Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
page |
integer | 1 | Page number |
limit |
integer | 50 | Results per page (max 500) |
entity_name |
string | — | Filter by entity |
Response:
{
"success": true,
"cases": [
{
"case_id": "CASE-abc123",
"entity_name": "Primary Site",
"case_type": "lateral_movement",
"status": "OPEN",
"severity": "CRITICAL",
"primary_user": "jsmith",
"affected_computers": ["WS-101", "WS-102", "DC-01"],
"alert_count": 47,
"max_risk_score": 85,
"first_alert_time": "2026-03-12T14:00:00",
"last_alert_time": "2026-03-13T02:30:00",
"created_at": "2026-03-12T14:05:00"
}
],
"pagination": {
"total_records": 11,
"total_pages": 1,
"current_page": 1,
"per_page": 50,
"has_next": false,
"has_previous": false
}
}
Get Case Detail
GET /api/cases/{case_id}
Returns full case with alerts, risk breakdown, and MITRE coverage.
Search Cases
GET /api/cases/search?severity=CRITICAL&status=OPEN&case_type=lateral_movement&entity_name=Primary%20Site&limit=100
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
status |
string | OPEN, IN_PROGRESS, CLOSED |
severity |
string | CRITICAL, HIGH, MEDIUM, LOW |
case_type |
string | Correlation type (e.g., lateral_movement, password_spraying, ransomware_indicators) |
entity_name |
string | Filter by entity |
assigned_analyst |
string | Filter by analyst |
page |
integer | Page number (default 1) |
limit |
integer | Results per page (default 100) |
Case Statistics
GET /api/cases/statistics?entity_name=Primary%20Site&timeframe=30d
Timeframe options: 1d, 7d, 30d, 90d, 180d, 365d, all
Response:
{
"success": true,
"summary": {
"total": 45,
"open": 11,
"in_progress": 5,
"closed": 29
},
"severity_breakdown": {
"CRITICAL": 8,
"HIGH": 15,
"MEDIUM": 18,
"LOW": 4
},
"entity_breakdown": [
{ "entity_name": "Primary Site", "count": 30 },
{ "entity_name": "Branch Office", "count": 15 }
]
}
Update Case Status
PUT /api/cases/{case_id}/status
Content-Type: application/json
{
"status": "CLOSED",
"assigned_analyst": "analyst1",
"resolution_notes": "Confirmed benign admin activity"
}
Bulk Close Cases
PUT /api/cases/bulk-status
Content-Type: application/json
{
"case_ids": ["CASE-abc123", "CASE-def456"],
"status": "CLOSED",
"notes": "Bulk close after review"
}
Trigger Correlation
Manually trigger the correlation engine (normally runs every 30 minutes).
POST /api/cases/correlate
Case Types
GET /api/cases/types
Returns all available correlation types with counts.
Export Cases
GET /api/cases/export?format=csv&entity_name=Primary%20Site&status=OPEN
Exports cases as CSV or JSON file download.
Ticket Integration API
Push incidents to Jira and ServiceNow.
Configuration
GET /api/integrations/jira/config — Get Jira configuration
PUT /api/integrations/jira/config — Update Jira configuration
POST /api/integrations/jira/test — Test Jira connection
GET /api/integrations/servicenow/config — Get ServiceNow configuration
PUT /api/integrations/servicenow/config — Update ServiceNow configuration
POST /api/integrations/servicenow/test — Test ServiceNow connection
GET /api/integrations/status — Get status of all integrations
Create Ticket
POST /api/incidents/{incident_id}/create-ticket
Content-Type: application/json
{
"system": "jira",
"incident_data": {
"incidentId": "INC-7601d999",
"groupKey": "jsmith",
"actorType": "user",
"entityName": "Primary Site",
"riskScore": 82,
"riskLevel": "critical",
"severity": "Critical",
"alarmCount": 156,
"threatCaseCount": 3,
"uniqueRules": ["Lateral Movement", "Credential Theft"],
"firstAlarm": "2026-03-12T18:26:28",
"lastAlarm": "2026-03-13T03:49:20",
"mitreTactics": ["Lateral Movement", "Credential Access"],
"mitreTechniques": ["T1021", "T1003"],
"investigationNotes": "Active investigation"
}
}
Response:
{
"success": true,
"ticket_id": "SEC-1234",
"ticket_url": "https://jira.example.com/browse/SEC-1234",
"system": "jira"
}
Preview Ticket
POST /api/incidents/{incident_id}/preview-ticket
Content-Type: application/json
{
"incident_data": { "..." : "same fields as create" }
}
Returns the ticket summary, description, and priority mappings without creating.
Webhooks (Inbound)
Receive status updates from external ticketing systems. No authentication required — these endpoints are called by Jira/ServiceNow.
POST /api/webhooks/jira — Receive Jira issue updates
POST /api/webhooks/servicenow — Receive ServiceNow incident updates
GET /api/webhooks/config — Get webhook setup instructions
Jira Status Mapping:
| Jira Status | RhythmX Status |
|---|---|
| Open, To Do, New, Backlog | OPEN |
| In Progress, Investigating | IN_PROGRESS |
| Done, Closed, Resolved | CLOSED |
| Won't Do, False Positive | FALSE_POSITIVE |
ServiceNow State Mapping:
| ServiceNow State | RhythmX Status |
|---|---|
| 1 (New) | OPEN |
| 2-5 (In Progress, On Hold, Awaiting) | IN_PROGRESS |
| 6-7 (Resolved, Closed) | CLOSED |
| 8 (Canceled) | FALSE_POSITIVE |
False Positives API
Manage false positive rules that suppress matching alerts.
CRUD Operations
GET /api/false-positives — List all FP rules
POST /api/false-positives — Create FP rule
GET /api/false-positives/{rule_id} — Get single rule
PUT /api/false-positives/{rule_id} — Update rule
DELETE /api/false-positives/{rule_id} — Delete rule
Create FP Rule
POST /api/false-positives
Content-Type: application/json
{
"created_by": "analyst1",
"fp_rulename": "Benign Admin RDP",
"title": "Admin RDP sessions from jump server",
"description": "Suppress RDP alerts from 10.0.0.50 to domain controllers",
"computer_name": "DC-01",
"ip_address": "10.0.0.50",
"expiration_days": "90"
}
Preview Matching Alerts
Preview which alerts would be suppressed before creating the rule.
POST /api/false-positives/preview
Content-Type: application/json
{
"rule_data": {
"computer_name": "DC-01",
"ip_address": "10.0.0.50"
}
}
Delete Matching Alerts
Permanently delete all alerts that match a specific FP rule.
DELETE /api/false-positives/{rule_id}/delete-matches
Audit Log API
Query the activity audit trail. Admin role required.
Query Audit Logs
GET /api/audit/logs?category=AUTH,INCIDENT&username=analyst1&start_date=2026-03-01&limit=100
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
category |
string | Comma-separated: AUTH, INCIDENT, HUNT, INVESTIGATION, CASE, ALARM, CONFIG, USER_MGMT, FALSE_POSITIVE, EXPORT, ADMIN |
action |
string | Specific action (e.g., LOGIN_SUCCESS, STATUS_CHANGE) |
username |
string | Filter by username |
resource_type |
string | Filter by resource type |
success |
string | true or false |
start_date |
string | ISO date or YYYY-MM-DD |
end_date |
string | ISO date or YYYY-MM-DD |
search |
string | Free-text search in details |
sort |
string | timestamp, username, category, action |
order |
string | asc or desc |
limit |
integer | Max 1000 (default 100) |
offset |
integer | Pagination offset |
Response:
{
"success": true,
"logs": [
{
"id": 1234,
"timestamp": "2026-03-13T04:15:00.123",
"username": "analyst1",
"user_role": "admin",
"category": "INCIDENT",
"action": "STATUS_CHANGE",
"resource_type": "incident",
"resource_id": "INC-7601d999",
"resource_name": "jsmith",
"details": {
"new_status": "IN_PROGRESS",
"notes": "Starting investigation"
},
"success": true,
"duration_ms": 45,
"result_count": null
}
],
"total": 1500,
"limit": 100,
"offset": 0,
"has_more": true
}
Audit Statistics
GET /api/audit/stats?days=7
Returns summary counts, activity by category, by user, by action, hourly/daily trends, and failed actions.
User Activity
GET /api/audit/user/{username}?days=7&limit=100
Export Audit Logs
GET /api/audit/export?format=csv&category=AUTH&start_date=2026-03-01
Downloads CSV or JSON file.
List Categories
GET /api/audit/categories
External Feed API
Read-only API for SOAR platforms and external SIEMs. Uses API key authentication — not JWT.
Full documentation: External Feed API
Endpoints
GET /api/v1/feed/health — Health check (no auth)
GET /api/v1/feed/incidents — List incidents
GET /api/v1/feed/incidents/{incident_id} — Get incident detail
GET /api/v1/feed/cases — List cases
GET /api/v1/feed/cases/{case_id} — Get case detail
Authentication: X-API-Key: smk_your_key_here
Rate Limit: 60 requests/minute per key (configurable)
Health API
System health monitoring. No authentication required.
Endpoints
GET /api/health/summary — Quick overview for dashboard
GET /api/health/logstash — Detection pipeline metrics & EPS
GET /api/health/system — CPU, memory, disk, load average
GET /api/health/services — All systemd service statuses
GET /api/health/database — MySQL connections, table sizes, retention
GET /api/health/storage — Disk usage for log directories
Health Summary
GET /api/health/summary
Response:
{
"overall_status": "healthy",
"logstash": {
"status": "running",
"eps_current": 22000,
"total_events": 1250000000,
"last_received": "2026-03-13T04:30:00Z"
},
"system": {
"cpu_percent": 35.2,
"memory_percent": 62.1,
"load_1m": 4.5
},
"services": {
"critical_down": 0,
"down_list": []
},
"disk": {
"root_used_percent": 45.3
},
"database": {
"status": "connected",
"alert_count": 15000000
}
}
Integration Configuration API
Configure platform integrations. Admin role required.
Syslog Forwarding (Alerts)
GET /api/integrations-config/syslog — Get alert syslog config
PUT /api/integrations-config/syslog — Update config & restart service
POST /api/integrations-config/syslog/test — Test syslog connection
GET /api/integrations-config/syslog/status — Service status & health
Incident/Case Syslog Forwarding
GET /api/integrations-config/incident-syslog — Get incident syslog config
PUT /api/integrations-config/incident-syslog — Update config & restart service
POST /api/integrations-config/incident-syslog/test — Test connection
GET /api/integrations-config/incident-syslog/status — Service status & health
PUT Body:
{
"host": "192.168.1.50",
"port": 514,
"protocol": "tcp",
"hostname": "RhythmX",
"poll_interval": 60
}
LogRhythm SQL Server
GET /api/integrations-config/logrhythm-sql — Get configuration
PUT /api/integrations-config/logrhythm-sql — Update & restart services
POST /api/integrations-config/logrhythm-sql/test — Test SQL connection
GET /api/integrations-config/logrhythm-sql/status — Connection & sync status
Error Responses
All endpoints return consistent error format:
{
"success": false,
"error": "Description of what went wrong"
}
HTTP Status Codes:
| Code | Meaning |
|---|---|
200 |
Success |
201 |
Created (promote incident) |
400 |
Bad request (missing/invalid parameters) |
401 |
Unauthorized (invalid or expired token/key) |
403 |
Forbidden (insufficient role) |
404 |
Resource not found |
429 |
Rate limit exceeded (external feed API) |
500 |
Internal server error |
Incremental Polling (SOAR Integration)
For SOAR platforms that poll RhythmX on a schedule, use timestamp-based filtering to fetch only new or changed data:
Polling for New Incidents
GET /api/incidents?since=2026-03-13T00:00:00Z&status=OPEN&limit=100
- On first poll, omit
sinceto get all current incidents - Record the
created_atof the most recent incident - On subsequent polls, pass that timestamp as
since
Polling for Updated Incidents
GET /api/incidents?updated_since=2026-03-13T03:00:00Z
Use updated_since to detect status changes, analyst assignments, and note updates since last poll.
Polling via External Feed API
The External Feed API offers the same capability with API key auth:
GET /api/v1/feed/incidents?since=2026-03-13T00:00:00Z&status=OPEN
X-API-Key: smk_your_key_here
See External Feed API for full details.