CW Automate / Ops Console — Knowledge Base
01

What This Tool Is

A real-time operations console for ConnectWise Automate — surfaces agent health, patch compliance, active alerts, and API latency in a single fixed-layout dashboard built for MSP NOC use.

Purpose & ScopeOps Dashboard

The ConnectWise Automate Ops Console is a single-page HTML dashboard that connects to the ConnectWise Automate REST API and presents a unified view of the managed endpoint environment. It is purpose-built for MSP NOC technicians, dispatch coordinators, engineers, and on-call staff who need situational awareness across all monitored clients without navigating the native Automate interface.

The design is read-first with action capability — the full environment state is visible at a glance, and operators can act immediately via remote commands, script execution, alert acknowledgment, and ticket creation, all without leaving the console. Every metric card links to the API call that produced it.

This tool is part of the KrawTech Stack of purpose-built operations dashboards alongside the SPX Console, ROC Leaders Board, and IVR Triage Console. In production it fetches live data through a configured proxy. Until that proxy is active it runs in demo mode using deterministic mock data so all interface and interaction patterns can be fully validated before go-live.

Who Uses ItAudience

NOC Technicians — Primary users. Monitor live agent status, triage incoming alerts, and execute remote commands without leaving the console. The alert feed and agent drawer are the main touch points during incident response.

Dispatch / Coordinators — Use the client breakdown and agent table to answer status questions instantly — "how many machines at Cascade Health are offline right now?" — without needing an Automate login.

Engineers — Script runner and API endpoints panel expose the underlying REST calls for testing, latency monitoring, and batch operations. Engineers can queue patch runs, verify API health, and test script targeting from a single pane.

Management — Six-metric KPI strip provides a summary view suitable for wall-board display or a morning briefing. No system access required to interpret the numbers.

Capability Matrix
CapabilityCurrent StatusNotes
Live agent inventory — search, filter, sortDemo-readyActivates on proxy go-live
Six KPI cards with 12-point sparkline historyDemo-readyActivates on proxy go-live
API endpoint health monitoring + latencyDemo-readyActivates on proxy go-live
Patch compliance donut with state breakdownDemo-readyActivates on proxy go-live
Active alert feed — Ack / Ticket / DismissDemo-readyTicket creation needs separate integration
24-hour agent check-in histogramDemo-readyActivates on proxy go-live
Remote commands (ping, sysinfo, reboot, isolate)SimulatedRequires proxy + agent command permissions
Script runner with scope targetingSimulatedRequires proxy + script execution rights
ConnectWise Control handoffLink onlyRequires Control URL config after proxy go-live
Ticket creation from alertSimulatedRequires CW Manage API or Automate Service module
30-second auto-refresh + countdown timerActiveWorks in demo and live mode
Simulate Agent Event / Outage (demo tools)ActiveDemo-only — removed or hidden in production
02

Integration Status

Current state of each integration layer. Live data flows end-to-end only after a valid proxy with injected authentication is wired to apiFetch().

⚠ Proxy Required
The dashboard currently runs in demo mode. All displayed data is generated by deterministic mock functions defined in the page script. Switching to live data requires zero changes to the render or interaction layer — only apiFetch() needs a real URL and Bearer token injection at the proxy.
Layer-by-Layer StatusProxy Pending
LayerStatusWhat Is Needed
Dashboard HTML / JS✓ ReadyNo changes required. All render logic, API path references, error handling, and UI states are fully implemented.
Proxy / MiddlewareNot configuredNeeds a reverse proxy or Express middleware that injects a valid Automate Bearer token and forwards requests to the Automate server URL.
Automate REST APINot reachableREST API must be enabled on the Automate server (default HTTPS). API user requires Computer (read), Alert (read/write), Script (read/execute), Patch (read), Client (read).
Auth / TokenNot configuredBearer token obtained via POST /automate/api/v1/apitoken. Expires every 24 hours. Proxy must handle refresh.
Remote CommandsNot activeActivates when proxy is live AND the API user has Command execution permissions on target agents.
Script ExecutionNot activeActivates when proxy is live AND the API user has Script execute rights.
Ticket CreationNot activeRequires CW Manage API integration or Automate's built-in Service module with its own auth scope.
Mock Data Generators (Active In Demo)Current State
FunctionWhat It ProducesReplaces
buildAgents()70–112 agents across 7 fixed clients with randomized OS, status, patch state, resource metrics, and timeline eventsGET /automate/api/v1/computers
buildAgentTimeline()3–5 ordered events per agent (offline event, alert trigger, patch notice, script run, check-in)Agent event history
mApiHealth()6 endpoint health records with simulated latency (22–260ms) and computed status thresholdsGET /automate/api/v1/health
mAlerts(agents)Up to 7 active alerts, filtered from agents where alerts > 0, further filtered by ackedAlerts SetGET /automate/api/v1/alerts
mPatches(agents)current / pending / critical / outdated counts by iterating a.patch across all agentsGET /automate/api/v1/patches
mActivity()24-element array — hours 8–17 use base 180–480, off-hours use 10–80, all with ±30 jitterAgent check-in event log
mMonitors()6 hard-coded monitor rules with randomized trigger counts each refreshGET /automate/api/v1/monitors
mClients(agents)Client roster with agent counts, sorted descending by countGET /automate/api/v1/clients
03

Architecture

A single self-contained HTML file. No build step, no npm dependencies, no server-side rendering. All logic runs in the browser. A proxy layer is the only external dependency needed for live data.

Browser
HTML · CSS · Vanilla JS
Single-file, zero deps
Proxy Layer
Bearer token injection
CORS bridging
⚠ NOT CONFIGURED
Automate REST
/automate/api/v1/*
Bearer auth, HTTPS
Automate Server
On-prem or hosted
SQL + agent database
Active (demo loop runs in-browser)
Requires proxy configuration
Not yet reachable
File AnatomySingle File
// Everything in one self-contained file: kb-dashboard-stack-cw-automate-DASHBOARD.html (1095 lines) │ ├── <head> │ ├── Google Fonts — Orbitron · Share Tech Mono · Rajdhani │ └── KrawTech design-system tokens (suite-standard :root block) │ ├── <style> │ ├── :root — all CSS custom properties (tokens) │ ├── body::before/after — 40px grid + scanline overlays │ ├── .hdr · .layout — header + grid scaffolding │ ├── .kpi-row · .kpi — KPI strip cards + sparklines │ ├── .panel · .ph · .pb — reusable panel component │ ├── .filter-bar · .f-chip · table — agent inventory table │ ├── .api-row · .pill — API endpoints panel │ ├── .donut-wrap · .dl-row — patch compliance donut │ ├── .cli-row — client bar chart rows │ ├── .script-runner · .sr-select — script runner UI │ ├── .exec-log · .exec-row — execution history log │ ├── .alert-item · .ai-* — alert feed items + actions │ ├── .score-row — top monitors bar chart │ ├── .act-bar · .act-tip — 24h check-in histogram │ ├── .drawer · .d-* — 440px slide-in detail drawer │ ├── .cmd-output — terminal output block │ ├── .toast-wrap · .toast — toast notification stack │ └── #ktc-demo-bar — demo warning banner │ ├── <body> │ ├── #ktc-demo-bar — HOME link + "DATA IS NOT LIVE" notice │ ├── .hdr — logo · sim buttons · refresh dot · timestamp │ └── .layout │ ├── .kpi-row (6 cards) │ └── .content (3-column grid) │ ├── .left-col — API health · Patch donut · Clients │ ├── .panel (center) — Agent table + check-in chart │ └── .right-col — Script runner · Alert feed · Monitors │ └── .drawer-overlay + .drawer (slide-in) + .toast-wrap │ └── <script> ├── State — agents[] · agentFilter · agentSort · hist{} ├── Utils — R() · rnd() · fmt() · hhmm() · now() ├── Mocks — buildAgents() · mApiHealth() · mAlerts() · … ├── Renderers — rKPI() · rApi() · rPatch() · rClients() · … ├── Interactions — setFilter() · sortBy() · openAgentDrawer() · … ├── Actions — remoteCmd() · repairAgent() · ackAlert() · … ├── Drawers — showKpiDrawer() · showApiDrawer() · closeDrawer() ├── apiFetch() — single proxy wire-up point (fetch + fallback) └── Refresh loop — refresh() · setInterval(30s) · countdown(1s)
Refresh Cycle30-Second Poll

refresh() is called on page load and every 30 seconds via setInterval. A parallel 1-second interval ticks the countdown label. The dot pulses green at rest, turns amber during the fetch window, returns to green on completion.

// Sequence inside refresh() : 1. dot → amber · label → "fetching…" 2. apiFetch('/automate/api/v1/computers', mock) → agents[] ↳ also applies a random status mutation on 40% of cycles 3. apiFetch('/automate/api/v1/health', mock) → apiHealth[] 4. apiFetch('/automate/api/v1/alerts', mock) → alerts[] 5. Local mocks (no fetch): mPatches · mClients · mMonitors · mActivity 6. rKPI() · rApi() · rPatch() · rClients() · rActivity() · rAlerts() · rMonitors() · renderAgents() 7. dot → green · label → "Live" · rtime → "@ HH:MM:SS"

apiFetch(url, mock) wraps every call in a 5-second AbortSignal.timeout. On any error or non-200 response it silently returns mock(), so the dashboard never renders an empty state.

In-Memory State Model
VariableTypePurpose & Scope
agentsArray<Object>Master agent roster. Built by buildAgents() on first refresh, mutated by simulate functions and the random status-mutation pass in refresh(). Persists across refresh cycles.
agentFilterstringActive filter chip value: 'all' | 'online' | 'offline' | 'alert' | 'patch'. Drives renderAgents().
agentSort{ field, dir }Active sort column name and direction (1 = asc, −1 = desc). Defaults to { field:'_idx', dir:1 } (insertion order). Persists across refreshes.
selectedAgentnumber | nullID of the agent whose row is highlighted and whose drawer is open. Set by openAgentDrawer(id), cleared by closeDrawer().
ackedAlertsSet<string>Set of acknowledged alert IDs (format ALT-NNNN). Items in this Set are filtered out of the alert list on every render. Not persisted across page loads.
histObject of arraysRolling 12-point history per KPI metric: total · online · offline · alerts · patches · scripts. Each entry appended on every rKPI() call; oldest dropped when length exceeds 12. Drives all sparkline SVGs.
execHistoryArray<Object>Last 8 script execution records. Each has { t, name, ok }. Prepended by logExec(); sliced to 8 max. Rendered into #execLog.
05

KPI Strip

Six equal-width metric cards spanning the full dashboard width. Each shows a current value, sparkline trend, delta label, and the backing API endpoint on hover. Clicking any card opens the detail drawer.

~85
Total Agents
GET /computers
~70
Online
GET /computers?online=true
~15
Offline
GET /computers?online=false
varies
Open Alerts
GET /alerts
varies
Patch Pending
GET /patches?status=pending
40–80
Scripts / 24h
GET /scripts/history
Card Anatomy
ElementCSSBehavior
Left accent barborder-left: 3px solid var(--ka)Color-coded per metric via --ka CSS variable set inline on each card.
Value.kpi-val — Orbitron 18px boldUpdated on every rKPI() call.
Sparkline SVG52×20px inline SVGPolygon area fill + polyline. Drawn from last 12 values in hist[metric].
Label.kpi-lbl — Rajdhani 11pxStatic uppercase descriptor.
Delta.kpi-d.up / .dn / .neuGreen for positive, red for negative, muted for neutral. Class toggled by rKPI() logic.
Endpoint tooltip.kpi-endpoint — opacity 0→1 on hoverShows the exact API path backing this metric. Fades in on hover.
Click handleronclick="showKpiDrawer(type)"Opens the detail drawer with API call block, full-width trend sparkline, and sample JSON response.
KPI Drawer — Three SectionsOn Click
SectionContent
API CallThe exact GET path with a simulated response time (20–120ms random).
Trend — last 12 pollsFull-width (360px viewBox) SVG sparkline of hist[type]. Color matches the KPI accent.
Sample ResponseFormatted JSON block showing real field names from the Automate API response shape for that endpoint. Values use live agents state for accuracy.
06

API Endpoints Panel

Left column, top slot. Monitors the health and response latency of the six core Automate REST endpoints. Polled every 30 seconds alongside all other data.

API EndpointsLeft Column · Top
Six rows rendered by rApi(list) from mApiHealth(). Each row: endpoint name (mono), latency in ms, status pill. Rows are clickable and open the API drawer. The panel header shows "polling…" until the first render completes.
GET /automate/api/v1/health — supplies the full list in production
Latency Thresholds
PillRangeCSS ClassMeaning
ok< 80ms.p-okEndpoint responding normally
warn80–159ms.p-warnElevated latency — monitor
deg160–279ms.p-degDegraded — investigate
dn≥ 280ms or error.p-dnDown or unresponsive
API Drawer — Three SectionsOn Click
SectionContent
Endpoint HealthCurrent latency in color-coded mono, P50 / P95 / P99 estimates (×0.7 / ×1.5 / ×2 of current ms), status pill.
Last 10 Requests10-bar mini histogram. Each bar height = min(100, v/400 × 100)%. Bar color follows the same thresholds (green/amber/orange/red).
AuthFormatted code block showing the full POST /apitoken call, request body shape, and the "→ 200 Bearer token (expires 24h)" response note.
07

Patch Compliance Panel

Left column, middle slot. SVG donut chart showing the distribution of patch states across all agents. Header shows overall compliance percentage.

Patch ComplianceLeft Column · Middle
Rendered by rPatch(p) from mPatches(agents). 72×72px SVG donut (r=28, stroke-width=10, four arcs drawn by drawDonut(segs)). Right-side legend: four rows with color dot, label, count. Header: "N% compliant".
Patch States
StateColor TokenCSS Badge ClassMeaningAction
Current--green.pb-okAll patches appliedNone required
Pending--hi (orange).pb-pendApproved, not yet installedSchedule maintenance window
Critical--red.pb-critSecurity-critical patches outstandingImmediate remediation
Outdated--amber.pb-warnData stale or scan not runTrigger patch scan via Script Runner

Compliance % formula: (current / total) × 100, rounded to nearest integer. Displayed in both the panel header and the donut center text.

08

Clients Panel

Left column, bottom slot. Horizontal bar chart showing agent count per managed client, sorted descending. Bar width is proportional to the maximum client count.

ClientsLeft Column · Bottom
Rendered by rClients(list) from mClients(agents). Each row: fixed 70px label (truncated), proportional cyan fill bar, numeric count. Bar width = (count / max) × 100% with a 0.6s CSS transition.
GET /automate/api/v1/clients
Demo Client Roster

Seven clients defined in the CLIENTS constant. Each receives 5–15 randomly assigned agents on every buildAgents() call. In production, this list and counts come from GET /clients.

const CLIENTS = [ 'Acme Corp', 'Harbor Tech', 'Sunrise MSP', 'BlueWave Inc', 'Redstone IT', 'Cascade Health', 'Nordic Systems' ];
09

Agent Inventory

Center panel — the primary operational table. All managed agents in a sortable, filterable, searchable table. Clicking any row opens the agent detail drawer.

Agent InventoryCenter Column · Full Height
Rendered by renderAgents(). Applies agentFilter, search query from #agentSearch, and agentSort before writing rows to #agentTbody. Full tbody replacement on every call. Agent count badge updates to the filtered row count.
GET /automate/api/v1/computers
Table Columns
ColumnFieldSortableRender Detail
Agenta.nameYesBold white. Format: {client_prefix}-{ws|srv|laptop|desktop|vm}-{10–99}
Clienta.clientYesRajdhani body font, muted color class .tsmall2
OSa.osNoAbbreviated: "Win 11", "Win 10", "Mac", "Ub. 22.04". Class .tsmall (mono)
Statusa.statusYesColored status dot + text. online=green/glow, alert=amber/glow, offline=red.
Patcha.patchNoColored .patch-badge. current=green, pending=orange, critical=red, outdated=amber.
Last Seena.lastSeenYesHuman string: online="0–4m ago", offline="1–71h ago". Class .tsmall.
Filter Chips
Chipdata-fFilter ConditionActive Style
AllallNo filter appliedCyan border + text
Onlineonlinea.status !== 'offline' — includes alert stateGreen border + text
Offlineofflinea.status === 'offline'Red border + text
Alertalerta.status === 'alert' onlyAmber border + text
Patchpatcha.patch !== 'current' — any non-current patch stateOrange border + text
Search

Input #agentSearch, placeholder "search agents, clients, OS…". Fires renderAgents() on every keystroke via oninput. Case-insensitive substring match across a.name, a.client, and a.os simultaneously. No debounce in the current implementation — see Limitations §17.

Agent Object Schema
FieldTypeDemo Range / Values
_idx / idnumberSequential starting at 1001. _idx used as default sort key.
namestring{client.split(' ')[0].toLowerCase()}-{type}-{10–98}
clientstringOne of 7 CLIENTS values
osstringWin 11 / Win 10 / Server 2022 / Server 2019 / macOS 14 / Ubuntu 22.04
statusstring'online' | 'alert' | 'offline'. 82% chance online; 22% of online become alert.
patchstring'current' | 'pending' | 'critical' | 'outdated'. Uniform random.
lastSeenstringOnline: "0–4m ago". Offline: "1–71h ago".
lastSeenMinnumberInteger minutes. Online: 0–4. Offline: 60–4319. Used for future sort support.
ipstring192.168.{1–9}.{10–253}
uptimestringOnline: "{1–89}d {0–22}h". Offline: "—"
cpu / ramnumberOnline: random in realistic range. Offline: 0.
disknumber25–94 (always set, even offline)
agentVerstring"23.{1–11}.{100–998}"
locationstringSite A / Site B / HQ / Remote / Branch
scriptsnumber0–11 (unused in current render)
alertsnumber0 for non-alert agents. 1–3 for agents with alert status.
timelineArray3–5 events from buildAgentTimeline(online, hasAlert, patch)
10

Agent Check-in Chart

Bottom sub-panel inside the center column, below the agent table. 24-bar histogram of check-in volume by hour. Business-hours peaks are highlighted.

Agent Check-ins — 24hCenter Column · Bottom Sub-panel
Rendered by rActivity(data) from mActivity(). 24 div bars in a flex row, height proportional to volume percentage of max. Hover shows a tooltip with hour and count. X-axis labels: 00:00 · 06:00 · 12:00 · 18:00 · now. Header shows peak hour and count.
Bar Coloring & Tooltip
ConditionBackground
v > max × 0.7rgba(0,212,255,.35) — dim cyan highlight
v ≤ max × 0.7var(--border2) — white-alpha 14%
Hover (any bar)var(--cyan) — full cyan accent

Tooltip class .act-tip is absolutely positioned above the hovered bar, shown via display:none → block on parent hover. Content: "{i}:00 — {v} check-ins".

11

Script Runner

Right column, top slot. Select a script and a target scope, then execute or schedule. Logs the last 8 executions in an inline history below the controls.

Activates When Proxy Is Live
In demo mode, both Run Script and Schedule produce toast notifications and log entries but make no real API calls. Once the proxy is active and the API user holds script execution rights, these buttons will call POST /automate/api/v1/scripts/{id}/run.
Script RunnerRight Column · Top
Two <select> dropdowns (script, target scope), two action buttons (Run, Schedule), and a scrollable execution log (#execLog, max-height 110px). Also accessible from the agent drawer via the "Run Script" footer button, which pre-fills the target with the selected agent.
POST /automate/api/v1/scripts/{id}/run
Script Library (Demo — Hard-Coded)
ValueScript NameTypical Use
1Disk CleanupFree disk space on Windows agents
2Restart SpoolerResolve print queue issues
3Flush DNS CacheClear stale DNS entries
4Run Windows UpdatesTrigger immediate Windows Update cycle
5Clear Event LogsPurge Windows event log files
6Check AV StatusVerify antivirus definitions and service state
7Restart Agent ServiceRecover an unresponsive Automate agent
8CPU / Memory SnapshotCapture resource usage at a point in time
Target Scopes
Option ValueLabelLive Behavior
allAll Online AgentsPass no computerId; API runs script against all online computers
clientSelected ClientPass clientId parameter to the script run endpoint
oneSelected AgentPass single computerId to the script run endpoint
Execution Log

Managed by logExec(name, ok). Each entry has three columns: timestamp (HH:MM), script name (truncated), status. Status values: .exec-s.run amber "running…" → replaced by .exec-s.ok green "✓ ok" or .exec-s.fail red "✗ fail". Failure probability in demo: ~10% (R() > 0.1).

12

Alert Feed

Right column, middle slot. Scrollable list of open alerts derived from agents with non-zero alert count. Three inline action buttons revealed on hover.

Partial Activation On Proxy Go-Live
The alert list and Ack action activate immediately when the proxy is live. Ticket creation (POST /service/tickets) requires a separate CW Manage API integration or Automate's Service module. Dismiss is always client-side only.
Alert FeedRight Column · Middle
Rendered by rAlerts(list). Alert count badge (#alertCnt) updates to the current list length. Items with ackedAlerts.has(id) render with class .acked (opacity 0.4). Empty state: centered mono "No active alerts". Left border color set via --aa CSS variable = sevColor(sev).
GET /automate/api/v1/alerts
Alert Item Fields
FieldDisplaySource
titleTop row, bold, truncated"Monitor alert — {a.name}" or "Agent offline — {a.name}"
agoTop row, right, mono mutedSame as a.lastSeen
bodySecond row, muted, truncated"{a.client} · {a.os} · {a.lastSeen}"
sevLeft border color via --aa1 = red (critical patch), 2 = orange (alert status), 3 = amber (other)
idUsed by all action buttons"ALT-{3000+i}" — incremented per alert in the pool
Alert Actions (Revealed on Hover)
ButtonFunctionStatusEffect
AckackAlert(id, e)Active (demo)Adds to ackedAlerts; adds .acked class to item. Toasts: "Alert acknowledged — PATCH /alerts/{id}"
→ TicketcreateTicket(id, e)SimulatedGenerates random ticket number; toasts "Ticket #NNNNN created — POST /service/tickets"; also acks the alert.
DismissdismissAlert(id, e)Active (demo)Adds opacity:0; transition then removes element from DOM after 300ms. Client-side only.

All three action buttons call e.stopPropagation() to prevent the click from bubbling to the alert item row click handler (which would trigger an agent drawer open).

13

Top Triggered Monitors

Right column, bottom slot. Six monitor rules ranked by trigger count. Each row shows a label, a proportional fill bar, and a numeric count.

Top Triggered MonitorsRight Column · Bottom
Rendered by rMonitors(list) from mMonitors(). Bar width = (n / max) × 100% with 0.6s CSS transition. Each monitor has its own accent color. Not individually clickable in the current implementation.
GET /automate/api/v1/monitors
Demo Monitor Rules
Monitor NameColor VariableDemo Range
CPU > 85% sustained--red8–21 triggers
Disk > 90% full--hi (orange)5–13 triggers
Agent heartbeat lost--amber4–10 triggers
Windows Update failed--purple3–8 triggers
AV definition outdated--cyan2–7 triggers
Event log errors >50--blue1–5 triggers
14

Detail Drawer

440px slide-in panel from the right. Three distinct content contexts: agent detail, KPI drill-down, and API endpoint health. Closes on overlay click or the × button.

Drawer Contexts
TriggerFunctionHeader Title / SubFooter
Click agent table rowopenAgentDrawer(id)Agent name / "Client · OS · IP"Repair Agent or Isolate · Run Script · Open in Control
Click KPI cardshowKpiDrawer(type)Metric title / endpoint pathClose button only
Click API endpoint rowshowApiDrawer(name, ms, st, path)Endpoint name / full pathClose button only
Agent Drawer — System Info Grid

Eight fields in a 2-column grid (.d-grid). Each is a .d-field block with a mono uppercase label and a value. CPU and RAM values are color-coded:

FieldColor Logic
Status, Uptime, Agent Ver, LocationPlain text, no color logic
CPU>80% red · 60–80% amber · <60% green
RAM>85% red · 70–85% amber · <70% green
DiskNo color logic — percentage only
PatchRendered as .patch-badge with full state styling
Remote CommandsSimulated

Six buttons in .d-cmd-row. Each calls remoteCmd(agentId, cmd). Output renders in #cmdOut as a .cmd-output terminal block. The initial state shows "POST /automate/api/v1/commands → agent {id}" then resolves after 900–1500ms.

CommandStyleSimulated Output
Ping.d-cmd.grnICMP reply with agent IP, TTL, and packet loss stats
Get Services.d-cmd.grnSpooler, W32Time, WinDefend, wuauserv status list
Screenshot.d-cmdCapture dimensions + storage path
Sys Info.d-cmdCPU%, RAM%, Disk%, OS, Uptime from live agent object
Reboot.d-cmd.redWarning + "Agent will reconnect in ~90 seconds"
Isolate.d-cmd.redIsolation confirmation + "POST /computers/{id}/isolate → 200 OK"
Footer Actions
ButtonConditionFunctionStatus
Repair AgentAgent is offlinerepairAgent(id) — toasts info, after 3.2s sets status=online + toasts success, re-renders tableSimulated
IsolateAgent is online or alertisolateAgent(id) — toasts "POST /computers/{id}/isolate" warningSimulated
Run ScriptAlways visiblerunScriptOn(id) — logs to execHistory, toasts "POST /scripts/1/run"Simulated
Open in ControlAlways visibleopenControl(id) — toasts "Opening ConnectWise Control…"Link only
Agent Timeline

A vertical timeline at the bottom of the agent drawer. Each event is a .d-tl-row with a dot, connecting line, HH:MM timestamp, and description string. Events are built by buildAgentTimeline(online, hasAlert, patch) at agent-creation time and include up to five entries: offline heartbeat loss, alert trigger, critical patch notice, scheduled script execution, and routine check-in. The most recent live event uses .d-tl-dot.live (cyan with glow).

15

All API Endpoints

Complete reference of every Automate REST endpoint called or referenced anywhere in the dashboard. All paths are relative to the Automate server base URL.

Authentication
POST /automate/api/v1/apitoken Obtain a Bearer token. Body: { "UserName":"…", "Password":"…", "TwoFactorPasscode":"…" }. Returns a token valid for 24 hours. Proxy must refresh before expiry. Proxy Required
Read (GET) Endpoints
GET/automate/api/v1/computersFull agent roster. Primary data source. Populates the agents array on every refresh. Drives KPI, table, patch donut, client bars, alerts, and activity chart.Proxy
GET/automate/api/v1/computers?online=trueOnline agents only. Referenced in KPI "Online" card endpoint tooltip and sample JSON response.Proxy
GET/automate/api/v1/computers?online=falseOffline agents only. Referenced in KPI "Offline" card endpoint tooltip.Proxy
GET/automate/api/v1/computers/{id}Single agent by ID. Called in agent drawer "API Calls Made" section for every opened agent.Proxy
GET/automate/api/v1/computers/{id}/patchesPatch list for a specific agent. Shown in agent drawer API calls section.Proxy
GET/automate/api/v1/computers/{id}/alertsAlerts for a specific agent. Shown in agent drawer API calls section.Proxy
GET/automate/api/v1/alertsAll open alerts. Feeds the Alert Feed panel and the "Open Alerts" KPI card. Polled every 30 seconds.Proxy
GET/automate/api/v1/clientsClient roster with agent counts. Feeds the Clients panel bar chart.Proxy
GET/automate/api/v1/scriptsAvailable scripts list. Currently the dropdown is hard-coded. In production this populates the #scriptSelect element dynamically on page load.Proxy
GET/automate/api/v1/scripts/historyScript execution history. Referenced in the "Scripts Run/24h" KPI card tooltip and sample response.Proxy
GET/automate/api/v1/patches?status=pendingPending patches. Referenced in the "Patch Pending" KPI card tooltip and sample response.Proxy
GET/automate/api/v1/healthAPI health endpoint. Polled every 30 seconds. Feeds the API Endpoints panel. If unavailable, mApiHealth() mock fills in.Proxy
GET/automate/api/v1/monitorsMonitor trigger data. Feeds the Top Triggered Monitors panel.Proxy
Write (POST / PATCH) EndpointsActivates When Proxy Is Live
POST/automate/api/v1/scripts/{id}/runExecute a script. Called by Script Runner buttons and runScriptOn() from drawer. Body includes computerId or scope. Currently simulated.Simulated
POST/automate/api/v1/scripts/{id}/scheduleSchedule a script for future execution. Called by the Schedule button. Currently simulated.Simulated
POST/automate/api/v1/commandsRemote command execution. Called by all six command buttons in the agent drawer. Output is currently simulated with static templates.Simulated
PATCH/automate/api/v1/alerts/{id}Acknowledge an alert. Called by the Ack button. Demo mode only updates the local ackedAlerts Set — no real API call is made.Demo Only
POST/automate/api/v1/computers/{id}/repairTrigger an agent repair task. Called by Repair Agent footer button when agent is offline. Currently simulated with a 3.2-second status mutation.Simulated
POST/automate/api/v1/computers/{id}/isolateNetwork-isolate an agent. Called by the Isolate command and Isolate footer button. Currently simulated with a toast notification.Simulated
POST/service/ticketsCreate a service ticket from an alert. Requires CW Manage API integration or Automate Service module. Separate auth scope from the main API user.Integration Required
16

Config & Fields Reference

All configurable constants, threshold values, and timing settings in the dashboard script. These can be changed without touching any render or interaction logic.

Script Constants
ConstantCurrent ValueEffect of Changing
CLIENTS7 client name stringsControls the client roster and agent name prefixes in demo mode.
OSS6 OS stringsOperating system pool for demo agents. Update to match your environment.
PATCH['current','pending','critical','outdated']Must match the exact values the Automate API returns for patch status.
setInterval (refresh)30000ms (30s)Lower to 15000ms for faster updates; raise to 60000ms to reduce API load.
AbortSignal.timeout5000ms (5s)Increase for slow on-prem Automate servers before falling back to mock.
hist max length12 pointsSparkline and trend window size. Increase for longer trend views.
execHistory max8 entriesRows visible in the script execution log.
mActivity() base range180–480 (peak hours), 10–80 (off-hours)Controls the shape of the 24-hour check-in histogram.
Threshold Values
ThresholdValueLocation in Code
Agent online probabilityR() > 0.18buildAgents() — 82% online, 18% offline
Agent alert probability (if online)R() > 0.78buildAgents() — 22% of online agents become alert
Random status mutation on refreshR() > 0.6refresh() — 40% of cycles mutate one agent status
API latency: OK< 80msmApiHealth()ms < 80 ? 'ok'
API latency: WARN80–159msmApiHealth()ms < 160 ? 'warn'
API latency: DEG160–279msmApiHealth()ms < 280 ? 'deg'
CPU red> 80%openAgentDrawer() — cpuColor logic
CPU amber60–80%openAgentDrawer() — cpuColor logic
RAM red> 85%openAgentDrawer() — ramColor logic
RAM amber70–85%openAgentDrawer() — ramColor logic
Activity bar highlight> 70% of peakrActivity()v > max * 0.7
Script execution failure rate~10%logExec()ok = R() > 0.1
apiFetch() — The Proxy Wire-Up Point

apiFetch(url, mock) is the single function that makes all real API calls. To activate live data, this is the only function that needs changing:

// Current implementation (demo-safe fallback): async function apiFetch(url, mock) { try { const r = await fetch(url, { signal: AbortSignal.timeout(5000) }); if (!r.ok) throw 0; return r.json(); } catch { return mock(); } // silent fallback } // Production: add BASE_URL prefix + Bearer token injection: const BASE_URL = 'https://your-proxy.example.com'; async function apiFetch(url, mock) { try { const r = await fetch(`${BASE_URL}${url}`, { headers: { 'Authorization': `Bearer ${TOKEN}` }, signal: AbortSignal.timeout(5000) }); if (!r.ok) throw 0; return r.json(); } catch { return mock(); } }
17

Documented Limitations

Known constraints in the current implementation. Each is documented with its root cause and the recommended resolution path.

Limitation Register
IDLimitationRoot CauseResolution
L-01All data is simulatedProxy not configured — apiFetch() always enters catch and returns mock output.Configure proxy with Bearer token injection. See §18.
L-02Agent count varies per page load (~70–112)buildAgents() calls rnd(5,16) per client on every first load. Not deterministic.Replaced by real GET /computers on proxy activation.
L-03No search debounceSearch fires renderAgents() on every keypress via oninput. Can cause perceptible lag with 500+ agents.Add 150ms debounce before production deployment.
L-04Acknowledged alert state lost on page reloadackedAlerts is an in-memory Set — not persisted to localStorage or API.Wire PATCH /alerts/{id} on proxy go-live so ack state persists server-side.
L-05Remote commands fully simulatedremoteCmd() uses a static outputs object — no POST /commands call is made.Replace static outputs with real API call after proxy + permission validation.
L-06Script dropdown is hard-coded8 scripts in HTML <option> elements — not fetched from GET /scripts.Populate dynamically from the Automate scripts API on page load.
L-07Ticket creation needs separate integrationPOST /service/tickets requires CW Manage API or Automate Service module — separate auth scope from the dashboard API user.Configure dedicated Manage API user and wire endpoint.
L-08No RBAC — all buttons visible to all usersStatic HTML file — any user who can load the page has full button access including Reboot and Isolate.Enforce at the proxy layer: restrict write endpoints by authenticated role.
L-09Patch counts computed client-side from agent datamPatches(agents) iterates the agent array. Accurate only if GET /computers includes patch state per agent.Replace with direct GET /patches call for accurate totals in production.
L-10Filter and sort state not persistedagentFilter and agentSort reset to defaults on every page load.Add localStorage read/write in setFilter() and sortBy() if persistence is needed.
L-11Full table re-render on every refresh + keypressrenderAgents() replaces entire #agentTbody innerHTML on every call. Causes full reflow.Add pagination (100-row pages + "Load more") or virtual scrolling for large environments.
L-12Open in Control is a stubopenControl(id) only shows a toast — no URL construction or window.open call.Add CWC server URL to config and build the Control session URL post-proxy.
18

Proxy Activation Checklist

Step-by-step process to move the dashboard from demo mode to live production data. Complete all steps in order before declaring the integration active.

Prerequisite
Verify the Automate REST API is reachable: https://your-automate-server/automate/api/v1/apitoken should return HTTP 200 or 401, not a connection error. If you get a timeout or TLS error, resolve server-side first.
Pre-Activation Steps
1
Create a dedicated Automate API user
In Automate → System → User Manager, create a user with minimum required permissions: Computer (read), Alert (read/write), Script (read/execute), Patch (read), Client (read). Do not use an admin account for the dashboard integration.
2
Validate the apitoken endpoint manually
Using curl or Postman: POST /automate/api/v1/apitoken with body {"UserName":"…","Password":"…","TwoFactorPasscode":""}. Confirm a Bearer token is in the response. Note the 24-hour expiry.
3
Deploy the proxy or middleware
Options: (a) nginx reverse proxy with auth header injection via proxy_set_header Authorization "Bearer $token"; (b) Node/Express middleware that wraps calls and handles refresh; (c) Cloudflare Worker if Automate is internet-accessible. The proxy must add CORS headers allowing the dashboard origin and inject Authorization: Bearer {token} on every forwarded request.
4
Implement token auto-refresh
Tokens expire every 24 hours. The proxy should re-authenticate proactively (e.g., every 23 hours via cron) or reactively (on receiving a 401 response). Do not hard-code the token in the dashboard HTML file.
5
Update apiFetch() in the dashboard script
Add a BASE_URL constant pointing to your proxy and update apiFetch() to prefix all paths. If the proxy handles auth injection, no Authorization header is needed in the browser-side fetch call. See §16 for the exact code change.
Validation Steps
6
Confirm GET /computers returns real agents
Open DevTools → Network tab. Trigger a manual refresh (click the dot). The /computers fetch should return HTTP 200 with a real JSON array. The KPI "Total Agents" count should match your actual managed environment.
7
Validate all six API Endpoints panel rows
Each row should show a real latency value. If any endpoint shows a random-pattern 22–260ms value (consistent with mock output), that fetch is still falling through to the mock generator — check the specific endpoint path and proxy routing.
8
Confirm alert feed shows real Automate alerts
Verify alert titles reference real agent names from your environment, not demo names like "acme-ws-42". If demo names appear, the alerts are still being generated from mock agent data.
9
Validate write endpoints in a safe environment first
Before enabling Reboot and Isolate in production, test POST /commands with the Ping command against an isolated test agent. Verify the real terminal output returns in the drawer. Only then enable destructive commands.
10
Remove or update the demo bar
Once live data is confirmed, update #ktc-demo-bar: change the amber notice to reflect the environment (e.g., "PRODUCTION — LIVE DATA"), or remove the element and its corresponding body { padding-top: 36px } rule entirely.
Go-Live Checklist
  • Dedicated Automate API user created with minimum required permissions
  • apitoken endpoint validated — HTTP 200 + Bearer token confirmed via Postman or curl
  • Proxy / middleware deployed with CORS configured for dashboard origin
  • Token auto-refresh implemented — cron or 401-triggered re-authentication
  • apiFetch() updated with BASE_URL prefix in dashboard script block
  • GET /computers returns real agents — confirmed in DevTools Network tab
  • All 6 API Endpoints panel rows show real, consistent latency values
  • Alert feed shows real Automate alert names — not demo agent names
  • Patch compliance donut reflects real environment patch state distribution
  • Write endpoints tested on isolated test agent before production enable
  • Script dropdown populated dynamically from GET /scripts
  • Demo bar updated or removed — body padding adjusted if removed
  • Ticket creation integration configured (CW Manage API or Automate Service module)
  • RBAC enforced at proxy layer — destructive commands restricted by role
19

Troubleshooting

Common issues during deployment and daily operation. Always open DevTools Console and Network tabs first — they will identify 90% of issues before any other diagnostic step is needed.

Dashboard still shows demo data after proxy is configured
Most common cause: The fetch URL still uses relative paths (/automate/api/v1/…) but the proxy is running on a different origin or port. Open DevTools → Network and look at the actual request URL. Verify the proxy is receiving the request — check proxy access logs. Also confirm the Authorization: Bearer … header is present in the outgoing request details. If the server returns any non-200 response code, apiFetch() silently falls back to mock. Add a temporary console.log inside the catch block to surface the real error.
Agent table is empty after switching to live data
Field name mismatch. The dashboard expects agent objects with specific field names: name, client, os, status, patch, lastSeen. The real Automate API returns computerName, clientName, etc. Add a normalization function between the apiFetch result and the agents array that maps Automate response fields to the dashboard schema. Also verify that buildAgents() is no longer being called — if agents.length === 0 after a failed or empty fetch, the mock is still running.
Refresh dot stays amber and never returns to green
One or more fetches are stalling. The 5-second AbortSignal.timeout prevents infinite hangs, but if every poll is timing out the dot will cycle amber repeatedly. Check Network tab for requests stuck in "pending". Verify the Automate server is reachable from the device's network — if behind a VPN, confirm connectivity. Consider temporarily increasing the timeout to 10000ms to rule out slow server startup. If only one endpoint is timing out, check that path specifically in the proxy routing config.
Clicking an agent row doesn't open the drawer
Check DevTools Console for JS errors during the last renderAgents() call. Common causes: (1) a null or undefined field on an agent object causing a crash in the row template literal; (2) the agent ID arriving as a string from the API rather than a number — agents.find(x => x.id === id) will fail a strict equality check in that case, so cast with Number(a.id) in the normalization layer; (3) a syntax error introduced during customization that broke the click handler binding.
API Endpoints panel shows "polling…" and never populates
The /health endpoint is unavailable, or the mock fallback is failing silently. The "polling…" label is the initial state of #apiLdr — it is only cleared when rApi() runs. Even in demo mode, mApiHealth() should populate the panel. If it remains empty, open Console and verify there is no error in rApi(). If the Automate instance does not expose a /health endpoint, replace that fetch in refresh() with a call to /computers and derive latency from the response time.
Acknowledged alerts reappear after the next refresh
Expected behavior in demo mode — this is a known limitation (L-04). ackedAlerts is an in-memory Set that persists within the session. mAlerts(agents) filters out IDs in that Set on every render, so acknowledged items should stay hidden within the same page session. However, if the agent data changes enough that a new alert gets a different ID, it will reappear. In production, PATCH /alerts/{id} must update the server-side state so the API stops returning the acked alert on subsequent polls.
Dashboard is slow or janky with a large agent count
Full DOM replacement on every cycle. renderAgents() replaces the complete #agentTbody innerHTML every 30 seconds and on every search keypress. For environments with 500+ agents this causes measurable layout reflow. Short-term: add pagination (show 100 rows, load-more button). Also add a 150ms debounce to the search oninput handler. Medium-term: switch to a DOM-diff approach that only updates changed rows rather than replacing all of them.
CORS errors in the browser console
The API is rejecting the request origin. Never call the Automate API directly from the browser — CORS will block it unless Automate is explicitly configured to allow your origin, which is not recommended. The proxy layer must add Access-Control-Allow-Origin: https://your-dashboard-domain (or * for development) to every proxied response. If using nginx: add_header 'Access-Control-Allow-Origin' '$http_origin'. If using a Cloudflare Worker, set the header in the Worker response. Verify with DevTools → Network → response headers for the failing request.
Simulate Outage / Agent Event buttons still showing in production
The demo buttons are not automatically hidden. simulateAgent() and simulateOutage() are bound to the "+ Agent Event" and "⚡ Simulate Outage" header buttons. These are harmless in production since they only mutate the in-memory agents array (real data will overwrite on the next 30-second refresh), but they are confusing to end users. Remove or hide both .hdr-btn elements in the header HTML, and optionally delete their corresponding functions from the script block.