MSP · CMD Suite KB // Huntress Operations Console
Knowledge Base v1.0 · 2026-03
MSP · CMD Suite — Stack Intelligence Layer
Huntress Operations Console
kb-dashboard-stack-huntress.html — Huntress REST API v1
A fullscreen, three-column security operations dashboard built for MSP engineers managing Huntress-protected client environments. Surfaces real-time threat detections, footholds, ransomware events, and open incidents in a single triage interface — no portal tab-switching required. Auto-refreshes every 30 seconds. Proxy-ready; runs entirely on mock data until a backend is deployed.
Huntress REST API v1 MDR / EDR Triage Multi-Client MITRE ATT&CK Mock-First / Proxy-Ready 30s Auto-Refresh
DEMO MODE — H.DEMO = true PROXY NOT YET LIVE UI FULLY FUNCTIONAL
📋
01 / OVERVIEW
What Is This Tool
5
API Endpoints Used
2
Write Endpoints
4
API Gaps Documented
🎯Purpose

The Huntress Operations Console replaces the need for MSP engineers to monitor the Huntress web portal directly. It aggregates agent health, active incidents, foothold detections, ransomware events, and process anomalies into a single always-on interface built for speed. Engineers filter by detection type, severity, or client. Clicking any row opens a full investigation panel with MITRE ATT&CK mappings, host context, process trees, and context-aware one-click actions.

The console is the Huntress-specific layer of the MSP · CMD stack. It is designed as a companion to the RMM and PSA dashboards — alerts are identified here, tickets and scripts are dispatched through the PSA/RMM layer.

📄File Details
Filenamestack-huntress.html
KB Filenamekb-dashboard-stack-huntress.html
SuiteMSP · CMD Design Suite — Stack Intelligence Layer
Dashboard Design SystemExo 2 / JetBrains Mono / Rajdhani (suite ground truth)
Mode at DeliveryDemoH.DEMO flag not present; mock branch inside hFetch()
Live ActivationUncomment return fetch(...) block inside hFetch()
Refresh Interval30 seconds — hStartRefreshTimer()
VendorHuntress Labs
API VersionHuntress REST API v1 — api.huntress.io/apidocs
Auth MethodHTTP Basic Auth — base64(apiKey:apiPassword) — proxy-injected
Mock-First Architecture. All data is generated by local JS mock functions (mockSummary(), mockAlerts(), mockDevices(), mockIncidents()) until the backend proxy is deployed. The UI is fully interactive with no API keys or server required. To go live, uncomment the three-line fetch() block inside hFetch() and comment out the setTimeout mock block below it.
🔌
02 / INTEGRATION
Integration Status
Current State: The console runs entirely on mock data. No live API calls are made. All fetch wrappers are written, commented, and waiting for a backend proxy. The mock data generators produce realistic-looking threat data so the UI is fully demonstrable. See the Proxy Activation section to go live.
📡Data Source Status by Route
Data SourceStatusProxy RouteUpstream Huntress API
Summary / KPI countsDemo/api/huntress/summaryProxy aggregates GET /v1/agents + GET /v1/incidents
Alert / Incident FeedDemo/api/huntress/alertsGET /v1/incidents
Agent / Device ListDemo/api/huntress/devicesGET /v1/agents
Typed Incident FilterDemo/api/huntress/incidentsGET /v1/incidents?incident_type=ransomware_canary,footholds
Host IsolationProxy NeededPUT /v1/agents/{id}/isolate — activates when proxy is live
Incident ResolveProxy NeededPATCH /v1/incidents/{id} — activates when proxy is live
SOC Report DownloadNo APINo Huntress REST endpoint — portal/email only
Remote Script ExecutionNo APINot in Huntress API — must use RMM platform
PSA Ticket CreationNo APINo Huntress outbound PSA API — must call PSA directly
🏗
03 / ARCHITECTURE
Architecture
┌────────────────────────────────────────────────────────────────────┐ │ HUNTRESS OPERATIONS CONSOLE — DATA FLOW │ └────────────────────────────────────────────────────────────────────┘ Browser (stack-huntress.html) │ └── DOMContentLoaded ├── hLoadAll() │ fires 4 hFetch() calls via Promise.allSettled() │ ├── hFetch('/api/huntress/summary') │ ├── hFetch('/api/huntress/alerts') │ ├── hFetch('/api/huntress/devices') │ └── hFetch('/api/huntress/incidents') │ │ │ MOCK (default)LIVE (uncomment fetch block) │ mockSummary() │ ► proxy at /api/huntress/* │ mockAlerts() │ ► injects Basic Auth header │ mockDevices() │ ► calls api.huntress.io │ mockIncidents() │ │ ├── hRenderAll() │ dispatches to 7 render functions └── hStartRefreshTimer() │ 30s countdown, re-fires hLoadAll() ┌────────────────────────────────────────────────────────────────────┐ │ BACKEND PROXY (server-side — not included in the .html file) │ └────────────────────────────────────────────────────────────────────┘ /api/huntress/summaryGET /v1/agents + GET /v1/incidents (aggregated) /api/huntress/alertsGET /v1/incidents ?status=open,investigating /api/huntress/devicesGET /v1/agents /api/huntress/incidentsGET /v1/incidents ?incident_type=ransomware_canary,footholds │ ▼ Authorization: Basic base64(apiKey:apiPassword) (injected by proxy) https://api.huntress.io (Huntress REST API v1) GAPS — require a different vendor API: Script Execution ► RMM API (NinjaRMM / CW Automate) — NOT Huntress Ticket Creation ► PSA API (ConnectWise Manage / Autotask) — NOT Huntress SOC Reports ► Portal/email only — no REST endpoint exists
JS State Object

All runtime state lives in the global H object at the top of the script block. The mock/live branch is controlled by the commented production block inside hFetch() rather than a single DEMO flag.

var H = { data: { summary: {}, // aggregated from /v1/agents + /v1/incidents alerts: [], // GET /v1/incidents (all types, open/investigating) events: [], // reserved — GET /v1/activity_logs if needed devices: [], // GET /v1/agents incidents: [] // GET /v1/incidents filtered by type }, selected: null, // currently selected row object filter: 'all', // type filter: all/foothold/ransomware/incident/process sev: 'all', // severity filter: all/crit/high/med/low countdown: 30, // seconds until next auto-refresh engineers: [...] // assignment pool for mock data };
🔗Key Functions
FunctionPurpose
hLoadAll()Fires all 4 hFetch() calls via Promise.allSettled(), stores results in H.data, calls hRenderAll(). Entry point for every data cycle.
hFetch(endpoint)Single fetch wrapper. Production block (commented out) calls fetch() with credentials. Mock block calls the appropriate mock*() generator via setTimeout.
hRenderAll()Master render dispatcher. Calls hRenderRail, hRenderKpis, hRenderApiHealth, hRenderPlatCards, hRenderStream, hRenderTable, hRenderTicker in sequence.
hStartRefreshTimer()Resets the 30s countdown bar animation and sets H.countTimer to decrement H.countdown every second. At zero, re-fires hLoadAll() and restarts.
hRefresh()Manual refresh. Clears timers, calls hLoadAll() and hStartRefreshTimer(). Bound to Refresh button and Ctrl+R.
hSelectRow(id)Finds row by ID across alerts + incidents, sets H.selected, re-renders table highlight, renders investigation panel.
hAct(type)Looks up ACTION_STEPS[type] and calls hRunModal().
an(el, to)Animated counter tween. Cubic ease-out, 500ms. Used by hRenderRail() for every rail counter.
📊
04A / PANELS
Rail Bar

The fixed top rail contains the brand badge, six animated stat counters, a live clock, and a manual Refresh button. All counters animate from their previous value to the new value using the an() cubic ease-out tween on every refresh cycle. A countdown progress bar below the rail fills from 100% to 0% over 30 seconds.

📡Rail Counter ElementsDemo
CounterElement IDSource FieldColor Class
Criticalrb-critsummary.critAlertsrv-red
Highrb-highsummary.highAlertsrv-orange
Mediumrb-medsummary.medAlertsrv-blue
Open Inc.rb-opensummary.openIncidentsrv-accent
Agentsrb-agentssummary.totalAgents — from X-Total-Count header on GET /v1/agentsrv-green
At Riskrb-risksummary.atRiskHostsrv-orange
Countdown bar: element #rfbar-fill uses a CSS width transition from 100% to 0% over 30s linear. hStartRefreshTimer() resets it by toggling transition:none briefly to snap back to 100%, then re-applies the 30s transition.
📰
04B / PANELS
Threat Ticker

A 22px marquee strip below the rail. Shows the eight most recent alert titles, hostnames, and timestamps scrolling left at 60s/cycle. The inner HTML is duplicated so the loop is seamless. Scroll pauses on hover. Rendered by hRenderTicker(alerts).

Ticker Properties
SourceTop 8 items from H.data.alerts
Element#ticker.tscroll
Label.tlbl — "LIVE THREATS" text in red
Scroll animationtscrl — 60s linear, pause on hover via animation-play-state
Critical color.tcvar(--red)
High/Med color.twvar(--orange)
Low/OK color.tokvar(--green)
📈
04C / PANELS
KPI Row

Six equal-width KPI cards in a horizontal grid below the ticker. Each has a count, a label, a contextual trend string, and a 2px top border in the card's accent color. Threshold logic determines the trend text. Rendered by hRenderKpis(s).

🧮KPI Cards & Thresholds
KPI LabelSource FieldThresholdTrend TextCSS Class
Critical AlertscritAlerts> 0▲ IMMEDIATE / — CLEARkpi-r
High AlertshighAlerts> 4▲ HIGH / — OKkpi-o
Footholdsfootholds> 0▲ ACTIVE / — NONEkpi-r
Ransomware Det.ransomwareDetections> 0▲ CRITICAL / — CLEARkpi-r
Open IncidentsopenIncidents> 5▲ HIGH / — OKkpi-o
At Risk HostsatRiskHosts> 10 / > 0▲ HIGH / ▲ ACT / — OKkpi-o
04D / PANELS
Left Column (290px)

The 290px left column (#lcol) contains three stacked sub-panels inside a flex column: API health indicators, queue filter buttons, and the incident stream scroll list.

🟢API Health Panel — #api-healthDemo

Five service health rows with colored status dots and latency values. Rendered by hRenderApiHealth(s). In live mode each row should reflect a real liveness probe to the associated endpoint.

RowLive CheckValidity
Huntress Agent APIHEAD /v1/agents✓ Valid endpoint
Portal APIGET /v1/organizations (liveness proxy)⚠ No SOC report API — see Limitations
Host Mgmt APIGET /v1/agents/{id}✓ Valid endpoint
Autorun AnalysisGET /v1/autorun_entries✓ Valid endpoint
Process InsightsGET /v1/process_groups✓ Valid endpoint
SOC Reports API row: Originally labeled "SOC Reports API" in the source. Audited and corrected — Huntress exposes no report download/list REST endpoint. The row was relabeled "Portal API" and its liveness check updated to GET /v1/organizations. SOC reports are portal and email only.
🔘Queue Filters

Two rows of filter buttons. Applied client-side against H.data.alerts and H.data.incidents — no re-fetch on filter change.

RowValuesState Key
Detection TypeAll / Incidents / Footholds / Ransomware / ProcessesH.filter
SeverityCRIT / HIGH / MED / LOW / ALLH.sev
📋Incident Stream — #inc-stream

Scrollable list of up to 20 combined alerts + incidents, newest at top. Each row shows timestamp, title, host/client, and severity badge. Clicking any row calls hSelectRow(id) which loads the right investigation panel. Rendered by hRenderStream(alerts). Live count shown in #stream-ct.

04E / PANELS
Center Column

The flex-1 center column (#ccol) contains: the platform status card row at top, the sortable alert grid table, and the six-button action bar at the bottom.

🃏Platform Status Cards — #prow

Eight flex cards across the top of the center column. Each card has a 2px top border that pulses red (animation: cp) when status is critical. Rendered by hRenderPlatCards(s, devices, incidents).

CardSource FieldCRIT ThresholdWARN Threshold
IncidentsopenIncidents> 3> 0
Footholdsfootholds> 0
RansomwareransomwareDetections> 0
AgentstotalAgents
At RiskatRiskHosts> 5> 0
IsolatedCount of devices where isolated === true
Auto-ResolvedautoResolvedToday
ProcessesblockedProcesses> 10
📁Alert Grid Table — #alert-table

Seven-column grid. Alerts + incidents merged, sorted by severity then age ascending. Left-edge 2px stripe colored by severity. Clicking a row highlights it and loads the investigation panel. Rendered by hRenderTable(); rows sourced from hGetRows() which applies H.filter and H.sev.

ColumnGrid WidthSource FieldNotes
ID70pxr.idHTR-XXXX (alerts) / INC-XXXX (incidents)
SEV48pxr.sevBadge: crit / high / med / low
TITLE / HOST1frr.title + r.hostIcon prefix by type: 💀 🫖 🚨 🩾
SOURCE88pxr.source"Huntress Agent" / "Huntress SOC"
STATUS80pxr.statusOPEN / INVEST. / RESOLVED / CLOSED
AGE58pxr.ageFormatted: Xm or XhYYm
ASSIGNED84pxr.assignedFrom H.engineers pool
🔬
04F / PANELS
Investigation Panel (310px)

The 310px right column (#rcol) is populated on row click. Provides full forensic context for the selected detection. Rendered by hRenderInvestigation(r).

🧾Investigation Panel Sections
SectionFieldsCondition
HeaderSeverity badge, title, ID, source, timestamp, type/tactic/MITRE badgesAlways shown
Detection Detailr.detail — full narrativeIf r.detail exists
Host & ProcessHostname, OS, PID, parent PID, command line, SHA256If r.host exists
Incident ScopeAffected host count, client nameIf r.hosts exists (incidents only)
MITRE ATT&CKTechnique ID, technique name, tacticIf r.mitreId exists
AssignmentEngineer, client, age, statusAlways shown
Event Timeline4–5 timestamped events from trigger to assignmentAlways shown
Recommended ActionsContext-aware buttons by severity/typeAlways shown
Recommended Action Logic
ConditionAction AddedStyle
sev === 'crit'Isolate Host from NetworkRed
type === 'foothold' or 'ransomware'Quarantine & ContainRed
AlwaysCreate PSA TicketGreen
AlwaysRun Remediation ScriptGreen
sev === 'crit' or 'high'Escalate to Tier 2Blue
AlwaysMark ResolvedGreen
04G / PANELS
Action Modal & Slide Pane

The action modal (#modal-ov) overlays the full screen. Steps animate through waiting → running → done states with a progress bar. Closed by the Complete button or Escape. The slide pane (#dpane) slides in 460px from the right with a dim overlay; also closed by Escape or clicking the dim overlay.

🚫
Demo only — all steps are simulated. In production, each step function should fire the real API call and resolve only on success. In particular, the Isolate Host modal will call PUT /v1/agents/{id}/isolate — a real network-impact action. Test only on non-production devices. Consider adding a confirmation dialog before proxy activation.
🌐
05 / API
Exact API Endpoints
All endpoints verified against Huntress REST API v1 at api.huntress.io/apidocs. Base URL: https://api.huntress.io. Auth: Authorization: Basic base64(apiKey:apiPassword).
GET/v1/incidents
Paginated list of all incidents across all organizations. Primary source for both /api/huntress/alerts and /api/huntress/incidents. The proxy differentiates them by applying status or incident_type query params.
✓ Confirmed valid — api.huntress.io/apidocs#tag/Incidents
GET/v1/incidents?incident_type=ransomware_canary,footholds
Filtered fetch for the typed incidents proxy route. Returns only ransomware canary and foothold types. Feeds platform status cards and the type filter buttons.
incident_type query param is documented and supported
GET/v1/agents
Paginated list of all agents across all organizations. Agent total count is read from the X-Total-Count response header, not the body array length. Provides device isolation status and risk context for platform cards.
✓ Confirmed valid — api.huntress.io/apidocs#tag/Agents
GET/v1/agents/{agent_id}
Single agent detail. Used by the Host Mgmt API health row liveness check. Also used in the isolate flow to confirm agent ID before issuing the isolation command.
✓ Confirmed valid
GET/v1/autorun_entries
Autorun persistence entries detected by agents. Powers the Autorun Analysis health row. Not yet surfaced in the main alert table — future expansion candidate for foothold detail.
✓ Confirmed valid — api.huntress.io/apidocs#tag/Autorun-Entries
GET/v1/process_groups
Process behavioral groups flagged by Huntress. Powers the Process Insights health row and feeds summary.blockedProcesses for the platform card.
✓ Confirmed valid — api.huntress.io/apidocs#tag/Process-Groups
GET/v1/organizations
List of organizations in the account. Used by the Portal API health row as a general liveness probe. No dedicated health/ping endpoint exists in the Huntress API.
✓ Confirmed valid
PUT/v1/agents/{agent_id}/isolate
Isolates a specific agent from the network. Called by the Isolate Host action. Real network-impact write operation. Activates when the backend proxy is live. Requires write-scoped API key.
✓ Confirmed valid — api.huntress.io/apidocs#tag/Agents
PATCH/v1/incidents/{incident_id}
Updates incident state. The Resolve action sends body {"status": "resolved"}. This is the only confirmed Huntress API endpoint for workflow state transitions on incidents. Activates when proxy is live.
✓ Confirmed valid — api.huntress.io/apidocs#tag/Incidents
N/ASOC Report Download / List
Huntress does not expose an API for accessing, listing, or downloading SOC reports. Reports are portal and email only. No GET /v1/reports or equivalent endpoint exists. The "Portal API" health row uses GET /v1/organizations as its liveness proxy.
✗ No endpoint — portal/email delivery only
N/ARemote Script Execution
Huntress does not provide an API to execute scripts on managed hosts. The agent is a detection/reporting sensor only. Script deployment must use an RMM platform. The "Run Script" action routes to RMM in production (NinjaRMM, CW Automate).
✗ No endpoint — use NinjaRMM POST /v2/device/{id}/script/runScript
N/AOutbound PSA Ticket Creation
Huntress has no API to create tickets in ConnectWise Manage, Autotask, or HaloPSA. Ticket creation must call the PSA's own API. The "Create Ticket" action calls the PSA directly; it does not pass through Huntress.
✗ No endpoint — use CW Manage POST /v4_6_release/apis/3.0/service/tickets
🔑
06 / API
Auth & Proxy Design
🔐Authentication

The Huntress REST API uses HTTP Basic Authentication. Credentials are an apiKey and apiPassword (not a username) base64-encoded in the Authorization header. Credentials must never appear in the frontend HTML file.

// Authorization header format Authorization: Basic base64(apiKey:apiPassword) // Proxy injection example — Node.js / Express const creds = Buffer.from( `${process.env.HUNTRESS_KEY}:${process.env.HUNTRESS_PASS}` ).toString('base64'); req.headers['Authorization'] = `Basic ${creds}`; // Test credentials directly with curl curl -u "yourKey:yourPassword" https://api.huntress.io/v1/organizations
Auth typeHTTP Basic Auth
Credentials formatapiKey:apiPassword — base64-encoded
Injection pointBackend proxy only — never the browser
API base URLhttps://api.huntress.io
Rate limitsNot publicly documented — monitor for HTTP 429
Pagination?page=1&limit=50 — total count in X-Total-Count header
🔀Proxy Route Map
Proxy RouteUpstream Call(s)Proxy Responsibility
/api/huntress/summaryGET /v1/agents
GET /v1/incidents
Aggregate: count incidents by severity, read X-Total-Count for total agents, compute at-risk host count
/api/huntress/alertsGET /v1/incidents?status=open,investigating&limit=50Pass through — normalize field names to match front-end data model
/api/huntress/devicesGET /v1/agents?limit=50Pass through — map isolated flag from agent status
/api/huntress/incidentsGET /v1/incidents?incident_type=ransomware_canary,footholdsPass through
07 / API
Limitations & Gaps
🚫
No SOC Report API. Huntress SOC reports (weekly summaries, incident PDFs, executive reports) are not accessible via the REST API. There is no GET /v1/reports or equivalent. Reports are delivered via email and the Huntress portal only. The "Portal API" health row uses GET /v1/organizations as its closest available liveness signal.
🚫
No Remote Script Execution. The Huntress agent is a passive detection and reporting sensor. It does not expose an endpoint to push and run scripts on managed hosts. All script-based remediation must be orchestrated through an RMM platform. The "Run Script" action in this console must route to NinjaRMM, CW Automate, or equivalent in production.
🚫
No Outbound PSA Integration via API. Huntress does not expose an endpoint to create tickets in ConnectWise Manage, Autotask, or HaloPSA. Ticket creation must call the PSA API directly. Alternatively, configure Huntress's native PSA integration in the Huntress portal to auto-create tickets on detection events — this does not require a custom API call.
No Single Summary Endpoint. The Huntress API has no /summary, /dashboard, or /stats endpoint. All KPI values must be computed by the proxy by aggregating GET /v1/incidents and GET /v1/agents. The summary proxy route makes at least two upstream calls per refresh cycle.
Pagination Required for Large Environments. Both GET /v1/agents and GET /v1/incidents are paginated with ?page&limit params. For MSPs managing 500+ agents the proxy should use X-Total-Count for display counts but may need multiple pages to build complete device lists. The frontend assumes the proxy returns a flat normalized array.
Rate Limits Undocumented. Huntress does not publicly document API rate limits. Monitor for HTTP 429 Too Many Requests in production. The 30-second refresh interval is intentionally conservative — increase to 60 seconds if 429s appear.
Dead State Key — H.data.events. The state object includes an events array that is never populated by any fetch call or mock generator. If event log data is needed in the future, the correct endpoint is GET /v1/activity_logs (confirmed valid in Huntress API docs).
08 / OPS
Proxy Activation Checklist
Current status: Demo mode. All items below must be completed before switching to the live fetch() block inside hFetch(). Features documented as "activates when proxy is live" are conditional on this checklist.
🔧Backend Proxy
  • Provision server-side proxy (Node.js/Express, Cloudflare Worker, or equivalent)
  • Store HUNTRESS_API_KEY and HUNTRESS_API_PASSWORD as env vars — never in the HTML file
  • Implement GET /api/huntress/alertsGET /v1/incidents?status=open,investigating&limit=50
  • Implement GET /api/huntress/devicesGET /v1/agents?limit=50
  • Implement GET /api/huntress/incidentsGET /v1/incidents?incident_type=ransomware_canary,footholds
  • Implement aggregation route GET /api/huntress/summary: fire both /v1/agents + /v1/incidents, compute severity breakdown, read X-Total-Count for agent total
  • Implement write route PUT /api/huntress/agents/:id/isolatePUT /v1/agents/{id}/isolate
  • Implement write route PATCH /api/huntress/incidents/:idPATCH /v1/incidents/{id}
  • Set CORS headers for the console's serving origin
  • Return consistent JSON error shapes for 4xx/5xx so Promise.allSettled branches correctly
  • Test all four GET routes against a live Huntress account — verify response shapes match what the mock generators return (same field names the render functions expect)
💻Frontend Activation
  • Open stack-huntress.html and locate hFetch(endpoint) in the script block
  • Uncomment the three-line production block: return fetch(endpoint, {credentials:'include', headers:{'Accept':'application/json'}})
  • Comment out (or remove) the return new Promise(function(resolve){ setTimeout(... mock block below it
  • Reload the console — the init toast should read "Live" not "Demo"
  • Verify rail counters populate with real values within 5 seconds
  • Verify alert table shows real Huntress incident IDs (not HTR-3000, INC-1100)
  • Test isolation on a non-production test device — confirm modal completes and isolation is reflected in the Huntress portal
  • Test resolve on a test incident — confirm status changes in the Huntress portal
💡What Activates When Proxy Is Live
FeatureChange
All counter valuesSwitch from randomized mock to real Huntress incident/agent counts
Alert tableActual detection titles, real host names, real MITRE technique IDs
API health dotsReal upstream latency from liveness probes
TickerReal alert titles and Huntress-assigned timestamps scrolling live
Isolate Host actionCalls PUT /v1/agents/{id}/isolate — real network-level isolation
Resolve actionCalls PATCH /v1/incidents/{id} — changes status in Huntress portal
Investigation panelReal host names, OS builds, actual process telemetry from Huntress agent
Auto-refreshPulls live data every 30 seconds instead of regenerating random mock values
🔩
09 / OPS
Config Fields
All Configurable Values in stack-huntress.html
Field / LocationDefaultPurpose
Mock/live branchMock setTimeout activeSwap by commenting/uncommenting the fetch() block inside hFetch()
Refresh interval30sControlled by hStartRefreshTimer() — hardcoded 30 in countdown logic. Increase if rate limiting appears.
H.engineers6 namesAssignment pool for mock data. Replace with real names or pull from PSA API.
KPI — footholds> 0 = CRITIn hRenderKpis(). Adjust if some footholds are an acceptable baseline.
KPI — open incidents> 5 = HIGHIn hRenderKpis(). Tune to client SLA expectations.
Plat card — at risk> 5 CRIT / > 0 WARNIn hRenderPlatCards(). Tune to environment baseline.
Ticker alert countTop 8hRenderTicker() — change slice(0,8)
Stream max items20hRenderStream() — change slice(0,20)
Counter tween duration500msIn an()Math.min(1,(now-t0)/500)
Toast duration4000msIn hToast()setTimeout(..., 4000)
Layout grid290px 1fr 310pxCSS #body grid-template-columns. Adjust column widths here.
Ticker scroll speed60sCSS .tscroll animation duration
10 / OPS
Action Buttons

Six action buttons appear in the bottom bar of the center column. All call hAct(type) which looks up a step definition in ACTION_STEPS and runs hRunModal(). In demo mode all steps are simulated with setTimeout. In live mode each step should fire a real API call and resolve on completion.

🔒Isolate HostAPI Valid

Locates the agent via GET /v1/agents, then calls PUT /v1/agents/{id}/isolate. Real network-impact action. Test only on non-production devices before activating with proxy. No confirmation dialog currently — consider adding one.

🚫Quarantine & ContainAPI Valid

Bulk isolation of multiple affected hosts. Same underlying call: PUT /v1/agents/{id}/isolate per host. Activates when proxy is live.

🄋Create PSA TicketNot Huntress API

Pulls context from GET /v1/incidents/{id}, then calls the PSA API directly. ConnectWise Manage: POST /v4_6_release/apis/3.0/service/tickets. Huntress has no outbound ticket API.

🔺EscalateManual / PSA

No Huntress escalation endpoint exists. In production: evaluate priority tier, page on-call analyst via PSA or notification service (PagerDuty/OpsGenie), attach investigation context as a ticket note.

📜Run ScriptNot Huntress API

Huntress has no script execution API. Route to NinjaRMM POST /v2/device/{id}/script/runScript or CW Automate equivalent. The original action step label "Deploy via Huntress agent" was corrected in the dashboard to "Deploy via RMM (NinjaRMM / CW Automate)".

Mark ResolvedAPI Valid

Calls PATCH /v1/incidents/{id} with body {"status":"resolved"}. Only confirmed Huntress REST write endpoint for workflow state. Change is reflected in the Huntress portal immediately. Requires write-scoped API key.

🔧
11 / OPS
Troubleshooting
Rail Counters Show Dashes After Load

Cause: hLoadAll() resolved but hRenderRail() received an empty summary object, or the promise chain threw before reaching hRenderAll().

Fix (demo): Check browser console for JS exceptions inside a mock generator function. In demo mode this should never occur.

Fix (live): Confirm the proxy is running and returning valid JSON from /api/huntress/summary with all expected fields: critAlerts, highAlerts, medAlerts, openIncidents, totalAgents, atRiskHosts.

Toast: "Some API Endpoints Unavailable"

Cause: One or more hFetch() calls returned a rejected promise (Promise.allSettled catches it and triggers the warning toast).

Fix (demo): Should never appear — a mock generator threw a runtime error. Check browser console for exceptions inside mockSummary(), mockAlerts(), etc.

Fix (live): The proxy returned a non-2xx status for at least one route. Check proxy logs, verify Huntress credentials, confirm the API key has read access to incidents and agents.

Alert Table Empty After Filter Reset

Cause: H.data.alerts and H.data.incidents are both empty arrays, or hGetRows() is filtering everything out.

Fix: Click "ALL" severity and "All" type to confirm filters are reset. Log H.data.alerts and H.data.incidents in the console — if both are empty, the fetch cycle did not complete successfully. In live mode verify the proxy returns non-empty arrays from both routes.

Investigation Panel Empty on Row Click

Cause: hSelectRow(id) could not find the clicked ID in the combined alerts + incidents array.

Fix: Check browser console for errors after clicking a row. Confirm the row ID format matches what is in H.data.alerts (demo: HTR-3000 style; live: Huntress UUIDs). Ensure hRenderTable() and hRenderStream() are using the same ID field as hSelectRow().

Countdown Bar Stuck / Auto-Refresh Not Firing

Cause: hStartRefreshTimer() was not called, or both H.refreshTimer and H.countTimer were cleared without being restarted.

Fix: Confirm hLoadAll() and hStartRefreshTimer() are both called in the DOMContentLoaded listener. Verify H.countTimer is not null via the console. Manually trigger hRefresh() with the Refresh button to confirm the data cycle works independently of the auto-timer.

Action Modal Stuck on "Waiting"

Cause: The next() inner function inside hRunModal() threw before firing, or ACTION_STEPS[type] returned undefined.

Fix: Check browser console for errors after triggering an action. Confirm the type string passed to hAct() is one of: isolate, contain, ticket, escalate, script, report, resolve.

Live Mode — 401 Unauthorized

Cause: API credentials are invalid, expired, or not being injected by the proxy.

Fix: Verify HUNTRESS_API_KEY and HUNTRESS_API_PASSWORD are set in the proxy environment. Confirm the proxy constructs Authorization: Basic base64(key:password) correctly — it is key and password, not a username. Test the credential directly: curl -u "yourKey:yourPassword" https://api.huntress.io/v1/organizations

Live Mode — Isolate or Resolve Returns 403

Cause: Write operations require an API key with write permissions. A read-only key returns 403 Forbidden.

Fix: In the Huntress portal under Account Settings, verify the API key has write scope. Generate a new key with write access if needed. Do not use write-access keys in shared or unauthenticated environments.