ConnectWise Automate Monitors — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited ConnectWise Automate Monitors (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Monitors in ConnectWise Automate continuously evaluate conditions on managed endpoints and generate alerts when thresholds are exceeded. This skill covers monitor types, threshold configuration, template management, and assignment strategies.
its own lifecycle, acknowledgment and history; use connectwise-automate-alerts.
are ConnectWise PSA ticket behaviour, not Automate thresholds; use connectwise-psa-tickets.
threshold on a device you point them at; discovering topology and watching links between devices is auvik-networks.
| Type | Description | Execution |
|---|---|---|
| Internal Monitor | Runs on the Automate server | Checks agent data |
| Remote Monitor | Runs from the Automate server | Network checks (ping, port, HTTP) |
| Agent Monitor | Runs on the endpoint agent | Local system checks |
| SNMP Monitor | Polls SNMP-enabled devices | Network device monitoring |
| Script Monitor | Executes script for check | Custom logic |
| Category | Examples |
|---|---|
| Performance | CPU, memory, disk usage |
| Service | Service status, process running |
| Event Log | Windows Event Log entries |
| Network | Ping, port open, HTTP response |
| Security | AV status, patch compliance |
| Hardware | Drive health, temperature |
| Application | Specific app monitoring |
| Level | Value | Description |
|---|---|---|
Information | 1 | Informational, no action needed |
Warning | 2 | Potential issue, investigate |
Error | 3 | Failure, action required |
Critical | 4 | Severe issue, immediate action |
See references/fields.md for the complete Monitor, MonitorTemplate, and MonitorStatus field reference.
Monitors are created either from a template (POST /Computers/{computerID}/Monitors with TemplateID) or as a fully custom definition (POST /Monitors with the full threshold/assignment payload). Thresholds always use one of the short operator codes — eq, ne, gt, lt, ge, le, contains, notcontains — not full words like "greater". Assignment targets a Group, Computer, or Client via AssignmentType + TargetID.
GET /cwa/api/v1/Monitors/Status?condition=Status ne 'OK'&pageSize=100
Authorization: Bearer {token}See references/api.md for the complete endpoint catalog (templates, per-computer monitors, status, create/update/disable/delete, group assignment).
async function createDiskSpaceMonitor(client, computerId, options = {}) {
const {
drive = 'C:',
warningThreshold = 15,
criticalThreshold = 5,
checkInterval = 300
} = options;
const monitor = await client.request('/Monitors', {
method: 'POST',
body: JSON.stringify({
Name: `Disk ${drive} Free Space`,
MonitorType: 'Agent',
Category: 'Performance',
CheckInterval: checkInterval,
FailAfter: 1,
ResetAfter: 1,
AlertSeverity: 2, // Warning
Thresholds: [
{
Field: 'DiskFreePercent',
Operator: 'lt',
Value: String(warningThreshold),
Duration: 0
}
],
AssignmentType: 'Computer',
TargetID: computerId
})
});
return monitor;
}async function createServiceMonitor(client, groupId, serviceName) {
const monitor = await client.request('/Monitors', {
method: 'POST',
body: JSON.stringify({
Name: `Service: ${serviceName}`,
MonitorType: 'Agent',
Category: 'Service',
CheckInterval: 300,
FailAfter: 2,
ResetAfter: 1,
AlertSeverity: 3, // Error
AlertMessage: `Service ${serviceName} is not running on %computername%`,
Thresholds: [
{
Field: 'ServiceStatus',
Operator: 'ne',
Value: 'Running'
}
],
AssignmentType: 'Group',
TargetID: groupId
})
});
return monitor;
}async function getFailingMonitors(client, clientId) {
// Get all computers for client
const computers = await client.request(
`/Clients/${clientId}/Computers?pageSize=500`
);
const failingMonitors = [];
for (const computer of computers) {
const monitors = await client.request(
`/Computers/${computer.ComputerID}/Monitors`
);
const failing = monitors.filter(m =>
['Warning', 'Error', 'Critical'].includes(m.Status)
);
if (failing.length > 0) {
failingMonitors.push({
computer: computer.Name,
computerId: computer.ComputerID,
monitors: failing.map(m => ({
name: m.Name,
status: m.Status,
value: m.CurrentValue,
lastCheck: m.LastCheck
}))
});
}
// Respect rate limits
await sleep(100);
}
return failingMonitors;
}async function applyTemplateToServers(client, templateId) {
// Get the template details
const template = await client.request(`/Monitors/Templates/${templateId}`);
// Get all servers
const servers = await client.request(
`/Computers?condition=OS contains 'Server'&pageSize=500`
);
const results = [];
for (const server of servers) {
try {
await client.request(`/Computers/${server.ComputerID}/Monitors`, {
method: 'POST',
body: JSON.stringify({ TemplateID: templateId })
});
results.push({
computer: server.Name,
status: 'applied'
});
} catch (error) {
results.push({
computer: server.Name,
status: 'failed',
error: error.message
});
}
await sleep(100);
}
return {
template: template.Name,
applied: results.filter(r => r.status === 'applied').length,
failed: results.filter(r => r.status === 'failed').length,
details: results
};
}async function getMonitorHealthSummary(client) {
const statuses = await client.request('/Monitors/Status?pageSize=1000');
const summary = {
total: statuses.length,
ok: 0,
warning: 0,
error: 0,
critical: 0,
unknown: 0,
disabled: 0,
byCategory: {}
};
for (const status of statuses) {
switch (status.Status) {
case 'OK': summary.ok++; break;
case 'Warning': summary.warning++; break;
case 'Error': summary.error++; break;
case 'Critical': summary.critical++; break;
case 'Unknown': summary.unknown++; break;
case 'Disabled': summary.disabled++; break;
}
// Track by category
const category = status.Category || 'Uncategorized';
if (!summary.byCategory[category]) {
summary.byCategory[category] = { ok: 0, issues: 0 };
}
if (status.Status === 'OK') {
summary.byCategory[category].ok++;
} else {
summary.byCategory[category].issues++;
}
}
summary.healthPercentage = Math.round(
(summary.ok / (summary.total - summary.disabled)) * 100
);
return summary;
}| Error | Status | Cause | Resolution |
|---|---|---|---|
| Template not found | 404 | Invalid TemplateID | Verify template exists |
| Invalid threshold | 400 | Malformed threshold | Check threshold syntax |
| Monitor exists | 400 | Duplicate monitor | Use unique name |
| Permission denied | 403 | No access | Check user permissions |
| Invalid operator | 400 | Bad comparison operator | Use valid operator |
See references/examples.md for a sample error response, a validateMonitorDefinition helper, and ready-made configurations for CPU, memory, service, and ping monitors.
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.