REST API Endpoints
AuroraSOC exposes 70+ REST endpoints via FastAPI at http://localhost:8000. All endpoints (except /health and /api/v1/auth/token) require authentication via JWT bearer or httpOnly session cookie. Service-to-service calls can also use X-API-Key on supported routes.
When to Use This Page
Use this page when you need a full endpoint map, expected auth and permission rules, and guidance for robust client integration behavior.
Authentication Flow
Base URL
http://localhost:8000/api/v1
Quickstart by Client Type
- cURL
- Python (httpx)
- TypeScript (fetch)
# 1) Authenticate and capture JWT
TOKEN=$(curl -s -X POST http://localhost:8000/api/v1/auth/token \
-H 'Content-Type: application/json' \
-d '{"username":"admin","password":"admin"}' | jq -r .access_token)
# 2) Query latest alerts
curl -s "http://localhost:8000/api/v1/alerts?limit=20" \
-H "Authorization: Bearer $TOKEN"
import httpx
BASE_URL = "http://localhost:8000/api/v1"
async def list_alerts() -> dict:
async with httpx.AsyncClient(timeout=20.0) as client:
# Authenticate once and reuse JWT for all protected endpoints.
auth_response = await client.post(
f"{BASE_URL}/auth/token",
json={"username": "admin", "password": "admin"},
)
auth_response.raise_for_status()
token = auth_response.json()["access_token"]
alerts_response = await client.get(
f"{BASE_URL}/alerts",
params={"limit": 20},
headers={"Authorization": f"Bearer {token}"},
)
alerts_response.raise_for_status()
return alerts_response.json()
const baseUrl = "http://localhost:8000/api/v1";
async function listAlerts() {
// Get a JWT first.
const authRes = await fetch(`${baseUrl}/auth/token`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ username: "admin", password: "admin" }),
});
if (!authRes.ok) throw new Error(`auth failed: ${authRes.status}`);
const { access_token } = await authRes.json();
const alertsRes = await fetch(`${baseUrl}/alerts?limit=20`, {
headers: { Authorization: `Bearer ${access_token}` },
});
if (!alertsRes.ok) throw new Error(`alerts failed: ${alertsRes.status}`);
return alertsRes.json();
}
Endpoints by Category
Authentication
| Method | Path | Auth | Permission | Description |
|---|---|---|---|---|
| POST | /api/v1/auth/token | None | — | Get JWT access token |
Request:
{
"username": "admin",
"password": "admin"
}
Response:
{
"access_token": "eyJhbGciOiJIUzI1NiIs...",
"token_type": "bearer"
}
Users
| Method | Path | Auth | Permission | Description |
|---|---|---|---|---|
| GET | /api/v1/users/me | JWT | — | Current user profile |
| GET | /api/v1/users | JWT | admin role | List all users |
| POST | /api/v1/users | JWT | admin role | Create user |
| DELETE | /api/v1/users/{username} | JWT | admin role | Delete user |
System
| Method | Path | Auth | Permission | Description |
|---|---|---|---|---|
| GET | /health | None | — | Public health check |
Alerts
| Method | Path | Auth | Permission | Description |
|---|---|---|---|---|
| GET | /api/v1/alerts | JWT | alerts:read | List alerts (paginated) |
| POST | /api/v1/alerts | JWT | alerts:write | Create alert |
| GET | /api/v1/alerts/{alert_id} | JWT | alerts:read | Get single alert |
| PATCH | /api/v1/alerts/{alert_id} | JWT | alerts:write | Update alert status |
| POST | /api/v1/alerts/{alert_id}/promote | JWT | alerts:write + cases:write | Promote alert into a new linked case |
| POST | /api/v1/alerts/{alert_id}/case-link | JWT | alerts:write + cases:write | Link alert to an existing case |
Query Parameters for GET /api/v1/alerts:
| Param | Type | Default | Description |
|---|---|---|---|
skip | int | 0 | Pagination offset |
limit | int | 50 | Page size (max 200) |
severity | string | — | Filter: critical|high|medium|low|info |
status | string | — | Filter: new|triaged|investigating|... |
source | string | — | Filter by source system |
Alert creation includes deduplication:
dedup_hash = SHA256(f"{title}|{source}|{sorted(iocs)}")
# If matching hash exists, returns existing alert instead of creating duplicate
Cases
| Method | Path | Auth | Permission | Description |
|---|---|---|---|---|
| GET | /api/v1/cases | JWT | cases:read | List cases |
| POST | /api/v1/cases | JWT | cases:write | Create incident case |
| GET | /api/v1/cases/{case_id} | JWT | cases:read | Get case with full workflow detail |
| PATCH | /api/v1/cases/{case_id} | JWT | cases:write | Update case |
| DELETE | /api/v1/cases/{case_id} | JWT | cases:delete | Delete case and detach linked alerts back to triaged |
| GET | /api/v1/cases/{case_id}/observables | JWT | cases:read | List case observables |
| POST | /api/v1/cases/{case_id}/observables | JWT | cases:write | Attach a case observable |
| GET | /api/v1/cases/{case_id}/evidence | JWT | cases:read | List case evidence |
| POST | /api/v1/cases/{case_id}/evidence | JWT | cases:write | Register evidence |
| POST | /api/v1/cases/{case_id}/evidence/{evidence_id}/custody | JWT | cases:write | Record custody handoff |
| GET | /api/v1/cases/{case_id}/comments | JWT | cases:read | List case comments |
| POST | /api/v1/cases/{case_id}/comments | JWT | cases:write | Add analyst comment |
| GET | /api/v1/cases/{case_id}/tasks | JWT | cases:read | List case tasks |
| POST | /api/v1/cases/{case_id}/tasks | JWT | cases:write | Create case task |
| PATCH | /api/v1/cases/{case_id}/tasks/{task_id} | JWT | cases:write | Update task lifecycle |
Create Case Request:
{
"title": "Lateral Movement via PsExec",
"severity": "high",
"description": "Multiple hosts showing PsExec activity",
"alert_ids": ["uuid-1", "uuid-2"],
"assignee": "analyst@example.com"
}
Promote Alert to Case Request:
{
"description": "Escalated from alert triage",
"assignee": "ir@example.com"
}
Update Case Request:
{
"title": "Credential theft escalation",
"severity": "high",
"status": "pending_approval",
"description": "Waiting for analyst approval before containment",
"assignee": "lead@example.com",
"requires_human_approval": true
}
Case Detail Response now includes first-class workflow collections:
{
"id": "case-uuid",
"title": "Credential theft escalation",
"status": "investigating",
"severity": "high",
"description": "Investigate suspicious identity activity",
"timeline": [{"agent": "api", "action": "case_created", "timestamp": "..."}],
"observables": [{"type": "domain", "value": "evil.example.com"}],
"evidence": [{"name": "Identity provider audit export", "custody_entries": [{"action": "registered"}]}],
"comments": [{"author": "analyst@example.com", "body": "Escalating to IR"}],
"tasks": [{"title": "Collect host triage package", "status": "done"}]
}
Attach an observable:
{
"type": "domain",
"value": "evil.example.com",
"source": "threat-intel",
"tags": ["c2", "credential-theft"],
"context": {"confidence": "high"}
}
Register evidence:
{
"name": "Identity provider audit export",
"evidence_type": "log_bundle",
"storage_url": "s3://aurora/evidence/idp-audit.json",
"sha256": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"location": "vault-a"
}
Update task lifecycle:
{
"status": "done",
"assignee": "ir@example.com"
}
Agents
| Method | Path | Auth | Permission | Description |
|---|---|---|---|---|
| GET | /api/v1/agents | JWT | agents:read | List all 16 agents |
| POST | /api/v1/agents/{id}/enable | JWT | agents:manage | Enable agent |
| POST | /api/v1/agents/{id}/disable | JWT | agents:manage | Disable agent |
| POST | /api/v1/agents/{id}/deploy | JWT | agents:manage | Deploy to site |
| PATCH | /api/v1/agents/{id}/scale | JWT | agents:manage | Scale instances (0-10) |
| POST | /api/v1/agents/investigate | JWT | investigation:trigger | Start investigation |
Agent filtering parameters:
| Param | Type | Description |
|---|---|---|
site | string | Filter by SOC site |
status | string | active|disabled|error |
type | string | Agent type enum |
Investigation request (rate-limited):
{
"alert_context": "Suspicious DNS queries to known C2 domain t1.evil.com from 10.0.1.50",
"has_cps_assets": false,
"severity": "high"
}
Sites
| Method | Path | Auth | Permission | Description |
|---|---|---|---|---|
| GET | /api/v1/sites | JWT | sites:read | List SOC sites |
| POST | /api/v1/sites | JWT | sites:manage | Register site |
| DELETE | /api/v1/sites/{id} | JWT | sites:manage | Remove site |
SIEM
| Method | Path | Auth | Permission | Description |
|---|---|---|---|---|
| GET | /api/v1/siem/logs | JWT | siem:read | Search logs |
| GET | /api/v1/siem/hunts | JWT | siem:read | List saved hunts |
| POST | /api/v1/siem/hunts | JWT | siem:search | Create a saved hunt from current search state |
| PATCH | /api/v1/siem/hunts/{hunt_id} | JWT | siem:search | Update hunt metadata, tags, or sharing configuration |
| POST | /api/v1/siem/hunts/{hunt_id}/run | JWT | siem:search | Run a saved hunt and return the latest matches |
| POST | /api/v1/siem/logs/{log_id}/promote | JWT | alerts:write and optionally cases:write | Promote a SIEM event into alert triage or a linked case |
| GET | /api/v1/siem/sources | JWT | siem:read | List log sources |
| GET | /api/v1/siem/stats | JWT | siem:read | SIEM statistics |
Create or update a saved hunt:
{
"title": "Suspicious PowerShell execution",
"description": "Recurring hunt for PowerShell download cradles",
"query": "powershell AND IEX",
"source": "endpoint_security",
"severity": "warning",
"host": "ws-042",
"sharing_mode": "roles",
"shared_with_roles": ["analyst", "operator"],
"tags": ["powershell", "execution"]
}
Detection Engineering
| Method | Path | Auth | Permission | Description |
|---|---|---|---|---|
| GET | /api/v1/detections/rules | JWT | detections:read | List shared and owned detection rules |
| POST | /api/v1/detections/rules | JWT | detections:write | Create a detection rule |
| PATCH | /api/v1/detections/rules/{rule_id} | JWT | detections:write | Update rule enablement, templates, sharing, pivots, or scheduler metadata |
| POST | /api/v1/detections/rules/{rule_id}/run | JWT | detections:read + siem:search | Evaluate a rule and return current matching logs |
| GET | /api/v1/detections/rules/{rule_id}/runs | JWT | detections:read | Return persisted manual and scheduled execution history |
| GET | /api/v1/detections/rules/{rule_id}/suppressions | JWT | detections:read | List active and historical suppressions for a rule |
| POST | /api/v1/detections/rules/{rule_id}/suppressions | JWT | detections:write | Create a suppression window and criteria set |
| GET | /api/v1/detections/rules/{rule_id}/feedback | JWT | detections:read | List analyst verdict history for a rule |
| POST | /api/v1/detections/rules/{rule_id}/feedback | JWT | detections:write | Record true_positive, false_positive, or needs_tuning feedback |
| GET | /api/v1/detections/metrics | JWT | detections:read | Return 24-hour detection trend metrics for the dashboard |
Create or update a detection rule with scheduled execution:
{
"name": "PowerShell download cradle",
"description": "High-signal PowerShell execution on workstations",
"query": "powershell.exe",
"source": "endpoint_security",
"match_severity": "warning",
"host": "ws-042",
"alert_severity": "high",
"auto_create_case": true,
"schedule_enabled": true,
"schedule_interval_minutes": 30,
"mitre_techniques": ["T1059.001"],
"sharing_mode": "team"
}
Manual run response now includes a persisted run record:
{
"rule": {
"id": "rule-uuid",
"name": "PowerShell download cradle",
"schedule_enabled": true,
"schedule_interval_minutes": 30,
"next_run_at": "2026-04-25T11:00:00+00:00"
},
"total": 4,
"suppressed_count": 1,
"run": {
"id": "run-uuid",
"trigger_mode": "manual",
"status": "completed",
"match_count": 4,
"suppressed_count": 1,
"sample_log_ids": ["log-1", "log-2"]
},
"logs": []
}
Detection metrics response:
{
"total_rules": 12,
"enabled_rules": 10,
"scheduled_rules": 6,
"runs_24h": 48,
"matches_24h": 121,
"suppressed_24h": 19,
"top_rules_by_matches": [
{"rule_id": "rule-a", "name": "PowerShell download cradle", "matches": 31}
],
"noisiest_rules": [
{"rule_id": "rule-b", "name": "Admin script allowlist candidate", "false_positives": 4}
]
}
Create a detection rule:
{
"name": "PowerShell download cradle",
"description": "Detects encoded PowerShell pulling remote content",
"query": "powershell AND IEX",
"source": "endpoint_security",
"match_severity": "warning",
"host": "ws-042",
"alert_severity": "high",
"alert_title_template": "Detection: {rule_name} on {host}",
"alert_description_template": "Matched event: {message}",
"auto_create_case": true,
"mitre_techniques": ["T1059.001", "T1105"],
"sharing_mode": "team",
"shared_with_roles": [],
"tags": ["powershell", "download-cradle"]
}
Create a suppression:
{
"name": "Suppress sandbox host",
"reason": "Lab machine generates known test traffic",
"criteria": {"host": "sandbox-01"},
"suppress_for_hours": 24
}
Record analyst feedback:
{
"verdict": "false_positive",
"alert_id": "alert-uuid",
"case_id": "case-uuid",
"siem_log_id": "log-uuid",
"notes": "Expected administrative automation",
"metadata": {"source": "siem_workspace"}
}
Promote a SIEM log under rule context:
{
"create_case": true,
"rule_id": "rule-uuid"
}
When the supplied rule has an active suppression matching the event, the promote route returns 409 instead of creating workflow objects.
EDR
| Method | Path | Auth | Permission | Description |
|---|---|---|---|---|
| GET | /api/v1/edr/endpoints | JWT | edr:read | List endpoints |
| GET | /api/v1/edr/endpoints/{id} | JWT | edr:read | Endpoint detail |
| POST | /api/v1/edr/endpoints/{id}/isolate | JWT | edr:manage | Isolate endpoint |
| POST | /api/v1/edr/endpoints/{id}/unisolate | JWT | edr:manage | Remove isolation |
| POST | /api/v1/edr/endpoints/{id}/scan | JWT | edr:manage | Trigger scan |
| GET | /api/v1/edr/stats | JWT | edr:read | EDR statistics |
SOAR
| Method | Path | Auth | Permission | Description |
|---|---|---|---|---|
| GET | /api/v1/soar/playbooks | JWT | soar:read | List playbooks |
| GET | /api/v1/soar/playbooks/{id} | JWT | soar:read | Playbook details |
| POST | /api/v1/soar/playbooks/{id}/toggle | JWT | soar:manage | Toggle enabled |
| POST | /api/v1/soar/playbooks/{id}/execute | JWT | soar:execute | Execute playbook or return pending_approval for critical playbooks |
| GET | /api/v1/soar/executions | JWT | soar:read | Execution history |
| GET | /api/v1/soar/rules | JWT | soar:read | Automation rules; returns an empty list outside dummy mode until a live rules backend exists |
| POST | /api/v1/soar/rules/{id}/toggle | JWT | soar:manage | Toggle rule; returns 503 outside dummy mode until a live rules backend exists |
Outside dummy mode, /api/v1/soar/playbooks and /api/v1/soar/executions are backed by the live PostgreSQL playbooks and playbook_executions tables. AuroraSOC no longer fabricates seeded SOAR playbooks in dry_run or real mode.
Critical playbook execution response:
{
"playbook_id": "11111111-2222-3333-4444-555555555555",
"playbook_name": "Contain critical endpoint",
"status": "pending_approval",
"approval_id": "approval-uuid",
"requires_approval": true,
"steps_total": 1,
"message": "Critical playbook execution is blocked pending human approval"
}
Playbooks
| Method | Path | Auth | Permission | Description |
|---|---|---|---|---|
| GET | /api/v1/playbooks | JWT | playbooks:read | List playbooks |
| POST | /api/v1/playbooks/{id}/execute | JWT | playbooks:execute | Execute playbook |
| GET | /api/v1/playbooks/executions | JWT | playbooks:read | Execution history |
CPS / IoT Devices
| Method | Path | Auth | Permission | Description |
|---|---|---|---|---|
| GET | /api/v1/cps/devices | JWT | cps:read | List CPS devices |
| GET | /api/v1/cps/devices/{id} | JWT | cps:read | Device detail |
| POST | /api/v1/cps/devices/{id}/revoke | JWT | cps:manage | Revoke certificate |
| GET | /api/v1/cps/attestations | JWT | cps:read | Attestation results |
Human Approvals
| Method | Path | Auth | Permission | Description |
|---|---|---|---|---|
| GET | /api/v1/approvals | JWT | approvals:manage | List pending approvals |
| POST | /api/v1/approvals/{id}/decide | JWT | approvals:manage | Approve/deny |
Decision request:
{
"approved": true,
"reason": "Verified - safe to isolate compromised host"
}
Reports
| Method | Path | Auth | Permission | Description |
|---|---|---|---|---|
| GET | /api/v1/reports | JWT | reports:read | List reports |
| GET | /api/v1/reports/{id} | JWT | reports:read | Full report |
Threat Intelligence
| Method | Path | Auth | Permission | Description |
|---|---|---|---|---|
| GET | /api/v1/iocs | JWT | iocs:read | List IOCs |
| POST | /api/v1/iocs | JWT | iocs:write | Create IOC |
Statistics & Dashboard
| Method | Path | Auth | Permission | Description |
|---|---|---|---|---|
| GET | /api/v1/stats/overview | JWT | stats:read | Dashboard overview |
| GET | /api/v1/dashboard/stats | JWT | stats:read | Flat stats for frontend |
| GET | /api/v1/correlations | JWT | cps:read | Physical-cyber events |
| GET | /api/v1/firmware | JWT | cps:read | Firmware inventory |
Rate Limiting
| Endpoint Category | Rate Limit | Window |
|---|---|---|
| General API | 100 req | per minute |
| Investigation trigger | 10 req | per minute |
| Playbook execution | 5 req | per minute |
Edge Cases and Behavioral Notes
Idempotency and dedup behavior
- Alert creation is dedup-aware and may return an existing record if a matching dedup hash already exists.
- Client applications should treat repeated alert submissions with identical payload fingerprints as potentially idempotent.
Pagination drift under active writes
- Offset pagination (
skip,limit) can drift if new rows are inserted between requests. - For polling dashboards, prefer refreshing from
skip=0or use stable sort criteria in client logic.
Partial outage behavior
- Some read endpoints may continue operating in degraded mode when a dependency is unavailable.
- For write endpoints during dependency degradation, return-path semantics should be treated as non-committed unless success is explicit.
Recommended Client Error Strategy
| Status | Typical Cause | Client Action |
|---|---|---|
| 400 | Invalid payload/schema | Correct request; do not retry blindly |
| 401 | Missing/expired token | Re-authenticate, retry once |
| 403 | Permission denied | Surface authz error; no automatic retry |
| 404 | Resource missing | Validate identifiers |
| 409 | Duplicate/conflict | Treat as idempotency/conflict signal |
| 429 | Rate limited | Exponential backoff with jitter |
| 503 | Dependency unavailable | Retry with capped backoff |
Versioning and Compatibility
- Stable API namespace:
/api/v1 - Current platform release line:
2.0.x - Backward-incompatible changes should be released under a new major API prefix
Create a thin SDK/client wrapper that centralizes token refresh, retry policy, and response validation. This improves consistency and reduces duplicated error-handling logic.
Error Responses
All errors follow a consistent format:
{
"detail": "Alert not found"
}
| Status Code | Meaning |
|---|---|
| 400 | Bad request / validation error |
| 401 | Missing or invalid JWT token |
| 403 | Insufficient permissions |
| 404 | Resource not found |
| 429 | Rate limit exceeded |
| 503 | Database unavailable (degraded mode) |
Degraded Mode
When PostgreSQL is unavailable, the API automatically falls back to in-memory data stores with demo data. This ensures the dashboard and monitoring remain functional during database maintenance.
@app.exception_handler(DatabaseUnavailable)
async def _handle_db_unavailable(request, exc):
return JSONResponse(
status_code=503,
content={"detail": "Database unavailable — running in degraded mode"},
)