MSP · CMD Suite KB // ConnectWise Automate Ops Console
Knowledge Base v1.0 · 2026-03
MSP · CMD Suite — Stack Intelligence Layer
ConnectWise Automate Ops Console
dashboard-stack-cw-automate.html — CW Automate REST API v1
A fullscreen, three-column RMM operations dashboard for MSP engineers managing ConnectWise Automate agent fleets. Surfaces agent status, patch compliance, open alerts, script execution, monitor triggers, and client breakdowns through the CW Automate REST API v1. Includes agent drill-down drawer with remote command execution, a 24-hour check-in activity chart, and sparkline history on all KPIs. Auto-refreshes every 30 seconds.
CW Automate REST API v1 RMM / MDM Triage Multi-Client Agent Management Script Runner Mock-First / Proxy-Ready
DEMO MODE — DEMO = true PROXY NOT YET LIVE UI FULLY FUNCTIONAL
📋
01 / OVERVIEW
What Is This Tool
8
API Endpoints Used
12
Audit Fixes Applied
3
Documented Limitations
🎯Purpose

The CW Automate Ops Console replaces direct portal monitoring for MSP operations teams managing RMM agent fleets. It combines agent inventory, patch compliance, alert feeds, script execution, and monitor trigger counts into a single always-on interface. Engineers can search, filter, and drill into any agent to see system metrics, run remote commands, and view the event timeline without switching tabs.

The console uses a three-column layout: left (API health, patch donut, client breakdown), center (agent inventory table + 24h check-in chart), and right (script runner, alert feed, top monitors). A slide-out drawer provides full agent detail and remote command output.

📄File Details
Filenamedashboard-stack-cw-automate.html
SuiteMSP · CMD Design Suite — Stack Intelligence Layer
FontsOrbitron (display), Share Tech Mono (mono), Rajdhani (body)
ModeDemoconst DEMO = true in JS
Live ActivationSet DEMO = false, uncomment apiFetch() production block
Refresh Interval30 seconds — startAutoRefresh(30000)
Vendor APIConnectWise Automate REST API v1
API Base Pathhttps://your-server.com/automate/api/v1
Auth MethodPOST /apitoken → Bearer token (24h); header: Authorization: CWA token=<bearer>
State PersistenceIn-memory only — filter, sort, selection reset on refresh
KTC Home BarRemoved — style block, div, and body{padding-top:36px} all stripped
🔌
02 / INTEGRATION
Integration Status
Current State: Full demo mode. All data generated from static JS mock functions. No live CW Automate API calls are made. 12 endpoint corrections were applied during the audit. The fetch wrapper, DEMO flag, and suite wiring pattern are all in place — see the Proxy Activation section to go live.
📡Data Source Status
Data SourceStatusLive API Endpoint
Agent listDemoGET /automate/api/v1/computers
Agent detailDemoGET /automate/api/v1/computers/{id}
Open alertsDemoGET /automate/api/v1/alerts
Client listDemoGET /automate/api/v1/clients
Scripts catalogDemoGET /automate/api/v1/scripts
Script historyDemoGET /automate/api/v1/commands
Patch statusDemoGET /automate/api/v1/patches
Monitor triggersDemoGET /automate/api/v1/monitors
Run script on agentProxy NeededPOST /automate/api/v1/computers/{id}/scripts/{scriptId}
Remote commandsProxy NeededPOST /automate/api/v1/computers/{id}/commands
Acknowledge alertProxy NeededPOST /automate/api/v1/alerts/{id}/ack
Schedule scriptProxy NeededPOST /automate/api/v1/schedules
Create ticketCW Manage APICW Manage: POST /v4_6_release/apis/3.0/service/tickets (separate system)
Network isolationNo APINo CW Automate endpoint — requires EDR/firewall integration
Health probeDerivedNo /health endpoint — liveness inferred from /computers response
🏗
03 / ARCHITECTURE
Architecture
┌────────────────────────────────────────────────────────────────┐ │ CW AUTOMATE OPS CONSOLE — DATA FLOW │ └────────────────────────────────────────────────────────────────┘ Browser (dashboard-stack-cw-automate.html) │ └── DOMContentLoaded ├── loadDashboard() │ suite entry point │ └── refreshAll() │ │ ├─ apiFetch('/computers') │ ├─ apiFetch('/alerts') │ ├─ mPatches / mClients / mMonitors / mActivity │ └─ rKPI / rApi / rPatch / rClients / rActivity / rAlerts / rMonitors / renderAgents └── startAutoRefresh(30000) │ 30s ► refreshAll() ┌────────────────────────────────────────────────────────────────┐ │ apiFetch(endpoint, mockFn) — fetch wrapper │ └────────────────────────────────────────────────────────────────┘ DEMO = true ► mock data from buildAgents() / mAlerts() etc. DEMO = false ► fetch('/api/automate' + endpoint) ► Backend Proxy ┌────────────────────────────────────────────────────────────────┐ │ Backend Proxy (not included in .html) │ └────────────────────────────────────────────────────────────────┘ /api/automate/* ► https://your-server.com/automate/api/v1/* Proxy injects: Authorization: CWA token=<bearer> AUTOMATE_URL env var ► CW Automate server base URL GAPS (no CW Automate REST endpoint available): Network isolation ► requires EDR/firewall integration Ticket creation ► CW Manage REST API (separate system) Health probe ► no /health endpoint; infer from /computers
Key State Variables & Entry Points
IdentifierTypePurpose
DEMOBooleanMaster mock/live switch. true = serve mock data. Set false + uncomment production fetch block to go live.
agentsArrayWorking copy of all agents. Built by buildAgents() on first load, then mutated by state-change simulations.
ackedAlertsSetAlert IDs that have been acknowledged. Session-only (not persisted).
agentFilterStringActive filter for agent table: all | online | offline | alert | patch
agentSortObject{ field, dir } — current sort column and direction.
histObjectSparkline history arrays for each KPI (max 12 samples, sliding window).
execHistoryArrayScript execution log (max 8 entries). Displayed in exec-log panel.
loadDashboard()FunctionSuite entry point. Called on DOMContentLoaded. Builds agents if empty, calls refreshAll().
refreshAll()Async FunctionDEMO/live branch controller. In demo: mutates state + calls all render functions. In live: fetches from proxy.
startAutoRefresh(30000)FunctionSets 30s setInterval on refreshAll() and a 1s countdown on the header label.
apiFetch(endpoint, mockFn)Async FunctionUnified fetch wrapper. Production block (commented out) calls /api/automate + endpoint with timeout. Falls back to mockFn().
🔥
04A / PANELS
Header Bar

A 44px fixed header containing the CW logo mark, title, and right-side controls. The right side includes two simulation buttons (Demo/testing only), a refresh indicator with pulsing green dot and countdown label, and a timestamp of the last sync. Clicking the dot/label triggers refresh() (alias for refreshAll()).

🔴Header Controls
ControlFunctionLive Behavior
+ Agent EventsimulateAgent()Demo only — randomly mutates an agent’s state and fires a toast
⚡ Simulate OutagesimulateOutage()Demo only — sets all agents for a random client to offline
Pulsing dot + labelrefresh()Manual refresh; triggers full refreshAll() cycle
TimestampUpdated in refreshAll()Shows @ HH:MM:SS of last successful data refresh
📈
04B / PANELS
KPI Strip

Six KPI cards in a 6-column grid. Each has a large number, a sparkline chart (last 12 polls), a label, and a delta badge. Hovering reveals the API endpoint tooltip. Clicking opens a detail drawer with the full endpoint, trend chart, and a sample JSON response. All six sparklines are maintained in the hist sliding window (12 samples max).

🏊KPI Definitions
KPIElement IDLive API EndpointNotes
Total AgentskTotalGET /computersTotal count from response pagination; delta shows daily new enrollments
OnlinekOnlineGET /computers?condition=online eq 1Corrected from ?online=true (invalid CW Automate param)
OfflinekOfflineGET /computers?condition=online eq 0Corrected from ?online=false
Open AlertskAlertsGET /alertsCount of open, unacknowledged alerts
Patch PendingkPatchesGET /patches?condition=status eq "pending"Corrected from ?status=pending
Scripts Run/24hkScriptsGET /commandsCorrected from non-existent /scripts/history
🌐
04C / PANELS
API Endpoints Panel

Left column, top. Displays six API endpoint health rows showing name, latency in ms, and a status pill (ok/warn/deg/dn). In demo mode all latencies are randomly generated on each refresh. Clicking a row opens the API detail drawer showing P50/P95/P99 latency, a 10-bar request history, and the auth endpoint reference.

No /health endpoint in CW Automate. The health check previously probed /automate/api/v1/health which does not exist. After the audit fix, liveness is inferred from the /computers response status. Status pills reflect mock latency in demo mode; in live mode they should reflect actual response time from the fetch wrapper.
🟢
04D / PANELS
Patch Compliance Donut

Left column, middle. A donut chart SVG with four segments (Current, Pending, Critical, Outdated) and a legend list. The center text shows the overall compliance percentage. In demo mode, patch states are assigned randomly to each generated agent. When live, this aggregates from GET /automate/api/v1/patches.

Donut Segments
SegmentColorSource FieldCSS Class
CurrentGreenpatch === 'current'pb-ok
PendingOrangepatch === 'pending'pb-pend
CriticalRedpatch === 'critical'pb-crit
OutdatedAmberpatch === 'outdated'pb-warn
🏠
04E / PANELS
Clients Panel

Left column, bottom (scrollable). Horizontal bar chart showing agent count per client, sorted descending. Bars are normalized to the largest client. In demo mode, 7 mock clients are generated (Acme Corp, Harbor Tech, Sunrise MSP, BlueWave Inc, Redstone IT, Cascade Health, Nordic Systems) with 5–15 agents each. Live: GET /automate/api/v1/clients.

📱
04F / PANELS
Agent Inventory Table

Center column, main panel. Sortable, filterable table of all agents. A search box filters by name, client, or OS. Filter chips narrow by status (Online/Offline/Alert) or patch state (Patch). Column headers for Agent, Client, Status, and Last Seen are sortable (ascending/descending). Clicking a row opens the agent detail drawer.

📊Table Columns
ColumnFieldNotes
Agenta.nameBold; sortable
Clienta.clientSortable
OSa.osAbbreviated for space: Win 11, Mac, Ub.
Statusa.statusColored dot + label: online (green), alert (amber), offline (red). Row fades to 55% opacity if offline.
Patcha.patchColored badge: Current/Pending/Critical/Outdated
Last Seena.lastSeenRelative time string; sortable by a.lastSeenMin
🔥
04G / PANELS
Agent Check-in Activity Chart

Bottom of the center panel (fixed height). 24 vertical bars representing agent check-ins per hour. Business hours (8–17) generate higher simulated counts (180–480). Bars above 70% of peak are highlighted in cyan. Hovering a bar shows the count in a tooltip. Peak hour is shown in the panel header. Generated fresh on each refresh by mActivity(). In live mode, this should be sourced from the /computers or /commands endpoint with timestamp aggregation.

📜
04H / PANELS
Script Runner

Right column, top. A two-dropdown + two-button interface for running and scheduling scripts. Script dropdown has 8 built-in options. Target dropdown selects scope: All Online Agents, Selected Client, or Selected Agent. An execution log below shows the last 8 script runs with time, name, and status (running… / ok / fail).

Script Runner ControlsProxy Needed
ControlFunctionLive API Call
▶ Run ScriptrunScript()POST /automate/api/v1/computers/{id}/scripts/{scriptId} — corrected from POST /scripts/{id}/run
⏰ SchedulescheduleScript()POST /automate/api/v1/schedules — corrected from POST /scripts/{id}/schedule
Exec loglogExec(name, status)Visual only — populated from execHistory array; no separate API call
Script targeting: The target dropdown selects “All Online Agents,” “Selected Client,” or “Selected Agent.” In live mode, running on multiple targets requires iterating the POST /computers/{id}/scripts/{scriptId} endpoint per computer. There is no batch script-run endpoint in the CW Automate API.
🔔
04I / PANELS
Alert Feed

Right column, middle (scrollable, grows to fill). Colored left-border alert cards sourced from mAlerts(agents). Three severity levels: 1=red (critical), 2=orange (warn), 3=amber (info). Three actions appear on hover: Ack, → Ticket, Dismiss.

🚨Alert Action Endpoints
ButtonOriginal (Wrong)Corrected
AckPATCH /alerts/{id}POST /automate/api/v1/alerts/{id}/ack
→ TicketPOST /service/tickets (CW Manage)CW Manage: POST /v4_6_release/apis/3.0/service/tickets — documented as limitation; not a CW Automate endpoint
DismissVisual onlyVisual only — removes from DOM, no API call
🔍
04J / PANELS
Top Triggered Monitors

Right column, bottom. Six monitor trigger rows with name, a normalized horizontal bar, and count. Generated by mMonitors() with fixed labels and random counts. In live mode: GET /automate/api/v1/monitors. The monitors endpoint is valid in CW Automate v1 API.

📄
04K / PANELS
Agent Detail Drawer

A 440px slide-in panel from the right, opened by clicking any agent row or KPI card. Overlays the UI with a dim background. The agent drawer shows API calls made, system info grid, remote command buttons, and a recent event timeline. KPI drawers show endpoint detail and sparkline trend.

Agent Drawer Sections
SectionContentAPI Source
API Calls MadeShows 3 GET calls: computer detail, patches, alerts for this agentGET /computers/{id}, /computers/{id}/patches, /computers/{id}/alerts
System InfoStatus, Uptime, Agent Ver, Location, CPU%, RAM%, Disk%, Patch badgeFrom agents array in memory
Remote CommandsPing, Get Services, Screenshot, Sys Info, Reboot, IsolatePOST /automate/api/v1/computers/{id}/commands
Recent Timeline2–4 timestamped events per agentBuilt by buildAgentTimeline()
🔨Drawer Footer Actions
ButtonOnline AgentOffline AgentLive API Call
Left buttonIsolate (red)Repair Agent (default)Isolate: no CW Automate endpoint — documented limitation. Repair: POST /automate/api/v1/commands
Run ScriptrunScriptOn(id)runScriptOn(id)POST /automate/api/v1/computers/{id}/scripts/1
Open in ControlopenControl(id)openControl(id)Opens ConnectWise Control (ScreenConnect) — no REST API call; URL-based launch
🌐
05 / API
Exact API Endpoints
All endpoints verified against CW Automate REST API v1 documentation. API base: https://your-server.com/automate/api/v1. Swagger UI: /automate/api/v1/swagger/ui/index.
POST/automate/api/v1/apitoken
Obtain a Bearer authentication token. Body: {"UserName": "…", "Password": "…", "TwoFactorPasscode": "…"}. Returns token valid for 24 hours. All subsequent requests use Authorization: CWA token=<bearer>.
✓ Confirmed valid
GET/automate/api/v1/computers
Returns paginated list of all managed computers (agents). Supports condition query parameter for filtering. Pagination via page and pagesize params. Response includes totalCount in header.
✓ Confirmed valid
GET/automate/api/v1/computers?condition=online%20eq%201
Filter for online agents. Audit fix: the original used ?online=true (a boolean string) which is not a valid CW Automate API parameter. Correct syntax uses the condition parameter with Automate query language: online eq 1.
✎ Corrected from ?online=true
GET/automate/api/v1/computers/{id}
Returns full detail for a single computer by ID. Used in the agent drawer to show system info, uptime, and resource usage.
✓ Confirmed valid
GET/automate/api/v1/computers/{id}/patches
Returns patch status records for a specific computer. Used in the drawer API calls section. Returns array of patch objects with name, KB, severity, and status.
✓ Confirmed valid
POST/automate/api/v1/computers/{id}/scripts/{scriptId}
Execute a specific script on a specific computer. Body may include parameters. Returns script job ID. Audit fix: original used POST /scripts/{id}/run which is not a valid CW Automate endpoint.
✎ Corrected from POST /scripts/{id}/run
POST/automate/api/v1/computers/{id}/commands
Send a remote command to a specific computer. Used by the Remote Commands buttons (Ping, Get Services, Screenshot, Sys Info, Reboot). Body specifies command type and parameters. Audit fix: original toast showed POST /automate/api/v1/commands without the computer ID sub-path.
✎ Corrected path format
GET/automate/api/v1/alerts
Returns open, unacknowledged alerts across all clients. Used in the alert feed and alerts KPI. Supports condition filtering. Response includes alert ID, severity, computer name, and message.
✓ Confirmed valid
POST/automate/api/v1/alerts/{id}/ack
Acknowledge an alert. Audit fix: original used PATCH /alerts/{id} which is not the correct CW Automate acknowledge verb. The CW Automate API uses a /ack sub-action via POST.
✎ Corrected from PATCH /alerts/{id}
GET/automate/api/v1/clients
Returns all clients (organizations) managed by the Automate instance. Used in the clients bar chart panel. Response includes client ID, name, and associated location count.
✓ Confirmed valid
GET/automate/api/v1/patches
Returns patch records across all computers. Supports filtering by status. The patch compliance donut and KPI card aggregate this data client-side. Pending filter: condition=status eq "pending".
✓ Confirmed valid
GET/automate/api/v1/scripts
Returns the scripts catalog. Used to populate the script runner dropdown in production. Each script object includes ID, name, and target scope.
✓ Confirmed valid
GET/automate/api/v1/commands
Returns command/script execution history. Used for the Scripts Run/24h KPI and exec log. Audit fix: original referenced GET /scripts/history which does not exist in the CW Automate API. Execution records are in /commands.
✎ Corrected from GET /scripts/history
GET/automate/api/v1/monitors
Returns monitor definitions and trigger counts. Used by the Top Triggered Monitors panel. In live mode, trigger frequency should be computed from condition=lastRunResult eq "fail" or similar.
✓ Confirmed valid
POST/automate/api/v1/schedules
Create a scheduled script run. Body specifies script ID, target computer(s), and schedule parameters. Audit fix: original used POST /scripts/{id}/schedule which is not a valid CW Automate endpoint.
✎ Corrected from POST /scripts/{id}/schedule
N/APOST /automate/api/v1/computers/{id}/isolate
No network isolation endpoint exists in CW Automate. Isolation must be handled by an EDR integration (Huntress, Microsoft Defender for Endpoint) or network/firewall policy. The Isolate button in the agent drawer now documents this limitation in the toast message.
✗ No endpoint — requires EDR/firewall integration
N/APOST /service/tickets
CW Automate does not have a PSA ticket creation endpoint. The “→ Ticket” alert action must call the ConnectWise Manage REST API: POST https://your-manage-server.com/v4_6_release/apis/3.0/service/tickets. This requires a separate CW Manage API key (clientId + public/private key pair). The alert action toast now documents this limitation explicitly.
✗ Not a CW Automate endpoint — use CW Manage API separately
🔧
06 / AUDIT
Audit Fixes Applied
12 fixes applied during the API audit. No render functions or UI layout were touched. All fixes are confined to endpoint strings, toast messages, comments, and the fetch/boot layer.
🗓Full Fix Log
#LocationOriginalFixed
1CSS & HTML#ktc-demo-bar style block, div, body{padding-top:36px}Removed entirely
2apiFetch() call/automate/api/v1/health (does not exist)Replaced with /computers probe; limitation documented in comment
3mApiHealth()Silently probed non-existent /healthAdded code comment documenting no /health endpoint exists
4KPI endpoint tags (HTML)?online=true / ?online=false?condition=online eq 1/0
5showKpiDrawer() map?online=true / ?online=false?condition=online%20eq%201/0
6repairAgent()POST /computers/{id}/repair (no such endpoint)POST /automate/api/v1/commands + limitation comment
7isolateAgent()POST /computers/{id}/isolate (no such endpoint)Toast now says “via EDR/firewall (not a CW Automate endpoint)” + comment
8runScriptOn()POST /scripts/1/run (wrong path)POST /automate/api/v1/computers/{id}/scripts/1
9scheduleScript()POST /scripts/{id}/schedule (wrong path)POST /automate/api/v1/schedules
10ackAlert()PATCH /alerts/{id} (wrong verb)POST /automate/api/v1/alerts/{id}/ack
11createTicket()POST /service/tickets (CW Manage endpoint, not Automate)Toast documents limitation: “via CW Manage API (not a CW Automate endpoint)”
12remoteCmd() output displayPOST /automate/api/v1/commands (missing computer ID sub-path)POST /automate/api/v1/computers/{agentId}/commands
🔑
07 / API
Auth & Proxy
🔐CW Automate Authentication

CW Automate uses a two-step token-based auth. First, POST credentials to get a Bearer token. Then include it in all subsequent requests. The token format differs from standard Bearer — it uses the CWA token= prefix in the Authorization header.

// Step 1 — obtain token const res = await fetch('https://your-server.com/automate/api/v1/apitoken', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ UserName: process.env.CWA_USER, Password: process.env.CWA_PASS, TwoFactorPasscode: '' }) }); const { CWAIISToken } = await res.json(); // Step 2 — all subsequent requests headers: { 'Authorization': `CWA token=${CWAIISToken}` }
Auth typeCW Automate proprietary token (NOT standard Bearer)
Header formatAuthorization: CWA token=<token>
Token lifetime24 hours; must be refreshed via POST /apitoken
Token storageProxy env vars: CWA_USER, CWA_PASS; never in HTML
2FAOptional; pass empty string if not required
🔀Proxy Route Design

The frontend calls /api/automate/*. The proxy injects auth headers and forwards to the CW Automate server. Token refresh should be handled server-side with a cached token renewed before expiry.

// Node.js / Express proxy sketch app.use('/api/automate', async (req, res) => { const token = await getCachedToken(); // cache + auto-refresh const upstream = `${process.env.AUTOMATE_URL}/automate/api/v1${req.path}`; const r = await fetch(upstream, { method: req.method, headers: { 'Authorization': `CWA token=${token}`, 'Content-Type': 'application/json', }, body: req.method !== 'GET' ? JSON.stringify(req.body) : undefined, }); res.status(r.status).json(await r.json()); });
08 / API
Limitations & Gaps
🚫
No Network Isolation Endpoint. CW Automate does not provide a REST API to isolate an agent from the network. The Isolate button in the drawer was originally wired to a non-existent POST /computers/{id}/isolate endpoint. In production, isolation must be handled by an integrated EDR (Huntress, Defender for Endpoint) or network infrastructure (VLAN reassignment, firewall rule push). The toast message now clearly documents this limitation.
🚫
No Ticket Creation Endpoint. CW Automate has no REST API for creating service tickets. Ticket creation requires calling the ConnectWise Manage REST API at a separate base URL using a different credential set (clientId + public/private key pair). The “→ Ticket” button on alerts must be wired to the Manage API, not Automate. This is a common source of confusion in the CW ecosystem as the two products share branding but have entirely separate APIs.
No Health Check Endpoint. CW Automate REST API v1 has no dedicated /health or /ping endpoint. Liveness must be inferred from the response status of a lightweight read endpoint like GET /computers?pagesize=1. The API health panel uses this approach after the audit fix.
Script History is in /commands, Not /scripts. CW Automate tracks all remote command and script executions in the /commands endpoint, not a dedicated /scripts/history sub-path. The Scripts Run/24h KPI was referencing a non-existent endpoint. Filter GET /commands by date range for daily execution counts.
Pagination Required for Large Deployments. All list endpoints (/computers, /alerts, /patches) return paginated results. Default page size is typically 100. For MSPs with 1000+ agents, the proxy must page through all results using ?page=N&pagesize=100 to build a complete dataset. The current frontend assumes the proxy returns a complete flat array.
Batch Script Execution Not Available. CW Automate provides no single API call to run a script on multiple computers simultaneously. Running a script on “All Online Agents” requires the proxy to iterate POST /computers/{id}/scripts/{scriptId} for each computer. This must be implemented as a server-side loop, not a single API call.
09 / OPS
Proxy Activation Checklist
Current Status: Demo mode. All items below must be completed before setting const DEMO = false.
🔓Backend Proxy Setup
  • Provision a server-side proxy (Node.js/Express, Cloudflare Worker, or equivalent)
  • Set environment variables: CWA_USER, CWA_PASS, AUTOMATE_URL (e.g. https://your-server.com)
  • Implement token acquisition: POST /automate/api/v1/apitoken → cache CWAIISToken
  • Implement token refresh logic (before 24h expiry)
  • Implement proxy route: /api/automate/*AUTOMATE_URL/automate/api/v1/* with Authorization: CWA token=<token>
  • Handle pagination: /computers with 100+ agents requires looping ?page=N&pagesize=100
  • Set CORS headers to allow requests from the console’s serving origin
  • Test GET /api/automate/computers → verify agent count matches Automate portal
  • Test GET /api/automate/alerts → verify alert list matches open alerts in Automate
💻Frontend Activation
  • Open dashboard-stack-cw-automate.html and locate const DEMO = true near the bottom of the script block
  • Set const DEMO = false
  • In apiFetch(), uncomment the production block (8 lines starting with try {)
  • Comment out (or delete) the demo fallback return new Promise(resolve => setTimeout(...)) block
  • Reload — the header label should show “Live” instead of “Demo”
  • Verify agent count in the Total Agents KPI matches the Automate portal
  • Verify the client breakdown bars reflect real client names
  • Test clicking an agent row → confirm API calls section in drawer shows real latency
  • Test Ack on an alert → confirm the POST call returns 200/204 from the proxy
💡What Activates When Proxy Is Live
FeatureChange When Live
KPI countersReal agent, alert, and patch counts from Automate
Agent tableReal device names, client names, OS versions, patch states
Alert feedReal open alerts from GET /alerts
Client breakdownReal client list and agent counts from GET /clients
Patch donutReal patch status distribution
Ack alertCalls POST /alerts/{id}/ack — real state change in Automate
Run ScriptCalls POST /computers/{id}/scripts/{scriptId} — real execution
Remote commandsCalls POST /computers/{id}/commands — real remote execution
30s auto-refreshPulls fresh data from all endpoints every 30 seconds
🔩
10 / OPS
Config Fields
All Configurable Values
Field / LocationDefaultPurpose
const DEMOtrueMaster mock/live switch. Set false + uncomment fetch block.
startAutoRefresh(30000)30 000 msRefresh interval. Increase to 60 000 for busy Automate instances.
CLIENTS array7 mock clientsIn production, sourced from GET /clients. Change mock names to match real clients for demo.
Agent pool size5–15 per clientbuildAgents()rnd(5,16). Adjust range for density.
Online ratio82% onlineR() > 0.18 in buildAgents(). 0.18 = 18% offline rate.
Alert ratio22% of online agentsR() > 0.78 in buildAgents(). Tune for demo realism.
Sparkline samples12if(arr.length>12)arr.shift() in rKPI(). Increase for longer trend window.
Exec log max8 entriesif(execHistory.length>8) execHistory.pop(). Increase for more history.
Activity chart hours24Array.from({length:24}...) in mActivity().
Peak hour coloring70% of maxv>max*.7 in rActivity(). Bars above this threshold turn cyan.
Agent table columns6 columnsAgent, Client, OS, Status, Patch, Last Seen. Add columns in renderAgents() and corresponding <th> elements.
Drawer width440pxCSS .drawer{width:440px}. Widen for more detail.
Toast duration3000mssetTimeout(...)},3000) in toast() function.
Fetch timeout8000msAbortSignal.timeout(8000) in apiFetch() production block. Adjust for slow Automate servers.
🔧
11 / OPS
Troubleshooting
KPI Counters Show Dashes After Load

Cause: loadDashboard() or refreshAll() was not called, or the agents array was empty when rKPI() ran.

Fix (demo): Check browser console for JS errors. Confirm buildAgents() returns a non-empty array. Verify DOMContentLoaded listener calls loadDashboard().

Fix (live): Confirm the proxy returns valid JSON from /api/automate/computers. Check that the response has a results array.

Agent Table Empty After Refresh

Cause: Active filter plus search text yields no matches, or agents array is empty.

Fix: Click the “All” filter chip. Clear the search box. Call renderAgents() from the console to verify the agents array is populated. In live mode, log agents in the console after a refreshAll() call to confirm data shape matches expectations.

Alert Ack Returns 404 in Live Mode

Cause: The CW Automate alert ID format from the mock (ALT-3000) differs from real Automate alert IDs (numeric). Or the proxy is not routing POST /alerts/{id}/ack correctly.

Fix: Verify the alert object from GET /alerts includes an id field (integer). Update the alert rendering to use the real ID. Confirm the proxy routes POST /api/automate/alerts/{id}/ackPOST /automate/api/v1/alerts/{id}/ack.

Script Runner Returns Error Toast

Cause: In live mode, the script ID from the select dropdown (value="1" through "8") are mock IDs that do not correspond to real scripts in the Automate instance.

Fix: In production, populate the script dropdown from GET /automate/api/v1/scripts and use real script IDs. The target computer must also be selected from real agent IDs from the agents array.

Proxy Returns 401 Unauthorized

Cause: Bearer token is expired, or the Authorization: CWA token= header format is wrong (standard Bearer prefix does not work with CW Automate).

Fix: Re-acquire the token via POST /automate/api/v1/apitoken. Confirm the header format is exactly CWA token=<tokenValue> (note: not Bearer). Implement token refresh in the proxy before the 24h expiry.

Drawer Does Not Slide Open

Cause: openAgentDrawer(id) could not find the agent in the agents array (ID mismatch), or the drawer/overlay elements are missing from the DOM.

Fix: Confirm agents.find(x => x.id === id) returns a truthy value. In live mode, agent IDs from the API must be stored as numeric values matching what is passed in row onclick. Check browser console for “Cannot read properties of undefined” errors.

Auto-Refresh Not Firing

Cause: startAutoRefresh() was not called, or the DOMContentLoaded event did not fire (e.g. script was blocking).

Fix: Confirm the script block ends with a document.addEventListener('DOMContentLoaded', ...) listener that calls both loadDashboard() and startAutoRefresh(30000). Manually call refreshAll() from the browser console to test the data cycle in isolation.

Patch Donut Shows 0% or All One Color

Cause: mPatches(agents) returned all zeroes (all agents have the same patch state), or total is zero causing a divide-by-zero in percentage calculation.

Fix (demo): Verify the PATCH constant array contains all four values and buildAgents() assigns random patch states. Confirm agents is not empty. Fix (live): Confirm the proxy returns patch records from /patches with a valid status field matching current|pending|critical|outdated.