AgentSkillsCN

Alerts

当您需要管理ConnectWise Automate告警时,可使用此技能:列出当前活动告警、确认告警、查看告警历史,并根据告警信息快速创建工单。本技能涵盖告警来源(监控器、脚本、事件)、告警严重程度分级、告警生命周期管理,以及工单与告警系统的无缝对接。

SKILL.md
--- frontmatter
description: >
  Use this skill when working with ConnectWise Automate alerts - listing active
  alerts, acknowledging alerts, viewing alert history, and creating tickets from
  alerts. Covers alert sources (monitors, scripts, events), alert severity levels,
  alert lifecycle management, and ticket integration.
triggers:
  - automate alert
  - automate notification
  - alert acknowledgment
  - alert history
  - alert ticket
  - monitor alert
  - labtech alert
  - automate incident

ConnectWise Automate Alert Management

Overview

Alerts in ConnectWise Automate are notifications generated by monitors, scripts, or system events that require attention. This skill covers alert listing, acknowledgment, history tracking, and ticket creation workflows.

Key Concepts

Alert Sources

SourceDescriptionExample
MonitorGenerated by monitor thresholdCPU > 90%
ScriptGenerated by script executionBackup failed
Event LogWindows Event Log triggerSecurity event
SystemAutomate system eventsAgent offline
ManualUser-created alertsMaintenance note

Alert Severity Levels

LevelValueDescriptionResponse Time
Information1Informational onlyReview at convenience
Warning2Potential issueInvestigate within hours
Error3Failure detectedRespond within SLA
Critical4Severe/emergencyImmediate response

Alert Lifecycle

code
Generated → Active → Acknowledged → Resolved
              │           │
              │           └── Ticket Created
              │
              └── Auto-Cleared (if condition clears)

Alert Status

StatusDescription
NewJust generated, unread
ActiveOpen, unacknowledged
AcknowledgedSomeone is working on it
ResolvedIssue fixed, alert closed
ClearedCondition auto-cleared
SuppressedTemporarily hidden

Field Reference

Alert Fields

typescript
interface Alert {
  // Identifiers
  AlertID: number;              // Primary key
  AlertGUID: string;            // Global unique ID

  // Source
  Source: AlertSource;          // Monitor, Script, EventLog, System
  SourceID: number;             // ID of source (MonitorID, ScriptID, etc.)
  SourceName: string;           // Name of source

  // Target
  ComputerID: number;           // Affected computer
  ComputerName: string;         // Computer hostname
  ClientID: number;             // Parent client
  ClientName: string;           // Client name
  LocationID: number;           // Location ID

  // Alert Details
  Subject: string;              // Alert title
  Message: string;              // Detailed message
  Severity: AlertSeverity;      // 1-4 severity level
  Status: AlertStatus;          // Current status

  // Timestamps
  TimeGenerated: string;        // When created
  TimeAcknowledged: string;     // When acknowledged
  TimeResolved: string;         // When resolved
  LastUpdate: string;           // Last status change

  // Acknowledgment
  AcknowledgedBy: string;       // User who acknowledged
  Notes: string;                // Acknowledgment notes

  // Ticket Integration
  TicketID: number;             // Linked ticket ID
  TicketStatus: string;         // Ticket status

  // Context
  Category: string;             // Alert category
  AdditionalData: object;       // Extra context data
}

type AlertSource = 'Monitor' | 'Script' | 'EventLog' | 'System' | 'Manual';
type AlertSeverity = 1 | 2 | 3 | 4;
type AlertStatus = 'New' | 'Active' | 'Acknowledged' | 'Resolved' | 'Cleared' | 'Suppressed';

Alert History Fields

typescript
interface AlertHistory {
  HistoryID: number;
  AlertID: number;
  Action: string;               // Status change, note added, etc.
  ActionBy: string;             // User who made change
  ActionTime: string;           // When action occurred
  PreviousStatus: string;
  NewStatus: string;
  Notes: string;
}

API Patterns

List Active Alerts

http
GET /cwa/api/v1/Alerts?condition=Status in ('New','Active')&pageSize=100
Authorization: Bearer {token}

Response:

json
[
  {
    "AlertID": 54321,
    "Subject": "Disk C: Low Space",
    "Message": "Disk C: is 8% free on ACME-DC01",
    "Severity": 2,
    "Status": "Active",
    "ComputerID": 12345,
    "ComputerName": "ACME-DC01",
    "ClientName": "Acme Corporation",
    "Source": "Monitor",
    "SourceName": "Disk Space Monitor",
    "TimeGenerated": "2024-02-15T08:30:00Z",
    "Category": "Performance"
  }
]

Get Alert Details

http
GET /cwa/api/v1/Alerts/{alertID}
Authorization: Bearer {token}

Response:

json
{
  "AlertID": 54321,
  "AlertGUID": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "Subject": "Disk C: Low Space",
  "Message": "Disk C: is 8% free on ACME-DC01\n\nTotal: 500 GB\nFree: 40 GB\nThreshold: 10%",
  "Severity": 2,
  "Status": "Active",
  "ComputerID": 12345,
  "ComputerName": "ACME-DC01",
  "ClientID": 100,
  "ClientName": "Acme Corporation",
  "LocationID": 1,
  "Source": "Monitor",
  "SourceID": 5001,
  "SourceName": "Disk Space Monitor",
  "TimeGenerated": "2024-02-15T08:30:00Z",
  "Category": "Performance",
  "AdditionalData": {
    "DriveLetter": "C:",
    "FreeSpaceGB": 40,
    "TotalSpaceGB": 500,
    "FreePercent": 8
  }
}

Filter Alerts by Client

http
GET /cwa/api/v1/Alerts?condition=ClientID = 100 and Status = 'Active'&pageSize=100
Authorization: Bearer {token}

Filter Alerts by Severity

http
GET /cwa/api/v1/Alerts?condition=Severity >= 3 and Status = 'Active'&pageSize=100
Authorization: Bearer {token}

Acknowledge Alert

http
POST /cwa/api/v1/Alerts/{alertID}/Acknowledge
Authorization: Bearer {token}
Content-Type: application/json

{
  "Notes": "Investigating disk space issue"
}

Response:

json
{
  "AlertID": 54321,
  "Status": "Acknowledged",
  "AcknowledgedBy": "admin@example.com",
  "TimeAcknowledged": "2024-02-15T10:45:00Z"
}

Resolve Alert

http
POST /cwa/api/v1/Alerts/{alertID}/Resolve
Authorization: Bearer {token}
Content-Type: application/json

{
  "Notes": "Cleared 50GB of temp files. Disk now at 18% free."
}

Add Note to Alert

http
POST /cwa/api/v1/Alerts/{alertID}/Notes
Authorization: Bearer {token}
Content-Type: application/json

{
  "Note": "Contacted user about large files in Downloads folder"
}

Create Ticket from Alert

http
POST /cwa/api/v1/Alerts/{alertID}/CreateTicket
Authorization: Bearer {token}
Content-Type: application/json

{
  "TicketSubject": "Disk Space Critical on ACME-DC01",
  "Priority": 2,
  "BoardID": 1,
  "Notes": "Auto-created from Automate alert"
}

Response:

json
{
  "AlertID": 54321,
  "TicketID": 98765,
  "TicketNumber": "TKT-2024-00123",
  "TicketStatus": "New"
}

Get Alert History

http
GET /cwa/api/v1/Alerts/{alertID}/History
Authorization: Bearer {token}

Response:

json
[
  {
    "HistoryID": 1,
    "Action": "Created",
    "ActionTime": "2024-02-15T08:30:00Z",
    "NewStatus": "New"
  },
  {
    "HistoryID": 2,
    "Action": "Acknowledged",
    "ActionBy": "admin@example.com",
    "ActionTime": "2024-02-15T10:45:00Z",
    "PreviousStatus": "Active",
    "NewStatus": "Acknowledged",
    "Notes": "Investigating disk space issue"
  }
]

Suppress Alert

http
POST /cwa/api/v1/Alerts/{alertID}/Suppress
Authorization: Bearer {token}
Content-Type: application/json

{
  "Duration": 3600,
  "Reason": "Scheduled maintenance window"
}

Bulk Acknowledge Alerts

http
POST /cwa/api/v1/Alerts/BulkAcknowledge
Authorization: Bearer {token}
Content-Type: application/json

{
  "AlertIDs": [54321, 54322, 54323],
  "Notes": "Bulk acknowledgment for server maintenance"
}

Workflows

Get Critical Alerts Dashboard

javascript
async function getCriticalAlertsDashboard(client) {
  const criticalAlerts = await client.request(
    `/Alerts?condition=Severity >= 3 and Status in ('New','Active')&pageSize=100`
  );

  const dashboard = {
    totalCritical: criticalAlerts.length,
    byClient: {},
    byCategory: {},
    oldest: null
  };

  for (const alert of criticalAlerts) {
    // Group by client
    const clientName = alert.ClientName || 'Unknown';
    if (!dashboard.byClient[clientName]) {
      dashboard.byClient[clientName] = [];
    }
    dashboard.byClient[clientName].push({
      id: alert.AlertID,
      subject: alert.Subject,
      computer: alert.ComputerName,
      severity: alert.Severity,
      age: getAlertAge(alert.TimeGenerated)
    });

    // Group by category
    const category = alert.Category || 'Uncategorized';
    dashboard.byCategory[category] = (dashboard.byCategory[category] || 0) + 1;

    // Track oldest
    if (!dashboard.oldest || new Date(alert.TimeGenerated) < new Date(dashboard.oldest.TimeGenerated)) {
      dashboard.oldest = alert;
    }
  }

  return dashboard;
}

function getAlertAge(timeGenerated) {
  const now = new Date();
  const generated = new Date(timeGenerated);
  const diffMs = now - generated;
  const diffMins = Math.floor(diffMs / 60000);

  if (diffMins < 60) return `${diffMins} minutes`;
  if (diffMins < 1440) return `${Math.floor(diffMins / 60)} hours`;
  return `${Math.floor(diffMins / 1440)} days`;
}

Acknowledge and Create Ticket Workflow

javascript
async function acknowledgeAndCreateTicket(client, alertId, options = {}) {
  const {
    notes = 'Acknowledged and ticket created',
    priority = 2,
    boardId = 1
  } = options;

  // Get alert details
  const alert = await client.request(`/Alerts/${alertId}`);

  // Acknowledge the alert
  await client.request(`/Alerts/${alertId}/Acknowledge`, {
    method: 'POST',
    body: JSON.stringify({ Notes: notes })
  });

  // Create ticket
  const ticketResponse = await client.request(`/Alerts/${alertId}/CreateTicket`, {
    method: 'POST',
    body: JSON.stringify({
      TicketSubject: alert.Subject,
      Priority: alert.Severity >= 3 ? 1 : priority,
      BoardID: boardId,
      Notes: `Auto-created from Automate alert\n\n${alert.Message}`
    })
  });

  return {
    alert: {
      id: alertId,
      subject: alert.Subject,
      status: 'Acknowledged'
    },
    ticket: {
      id: ticketResponse.TicketID,
      number: ticketResponse.TicketNumber
    }
  };
}

Alert Triage by Client

javascript
async function triageAlertsByClient(client, clientId) {
  const alerts = await client.request(
    `/Alerts?condition=ClientID = ${clientId} and Status in ('New','Active')&pageSize=200`
  );

  const triage = {
    client: clientId,
    total: alerts.length,
    critical: [],
    error: [],
    warning: [],
    info: []
  };

  for (const alert of alerts) {
    const summary = {
      id: alert.AlertID,
      subject: alert.Subject,
      computer: alert.ComputerName,
      source: alert.SourceName,
      age: getAlertAge(alert.TimeGenerated)
    };

    switch (alert.Severity) {
      case 4: triage.critical.push(summary); break;
      case 3: triage.error.push(summary); break;
      case 2: triage.warning.push(summary); break;
      default: triage.info.push(summary);
    }
  }

  return triage;
}

Bulk Alert Resolution

javascript
async function bulkResolveAlerts(client, alertIds, notes) {
  const results = [];

  for (const alertId of alertIds) {
    try {
      await client.request(`/Alerts/${alertId}/Resolve`, {
        method: 'POST',
        body: JSON.stringify({ Notes: notes })
      });
      results.push({ alertId, status: 'resolved' });
    } catch (error) {
      results.push({ alertId, status: 'failed', error: error.message });
    }

    // Respect rate limits
    await sleep(100);
  }

  return {
    resolved: results.filter(r => r.status === 'resolved').length,
    failed: results.filter(r => r.status === 'failed').length,
    details: results
  };
}

Alert Escalation Check

javascript
async function checkAlertEscalation(client) {
  const alerts = await client.request(
    `/Alerts?condition=Status = 'Active' and Severity >= 2&pageSize=500`
  );

  const escalations = [];
  const now = new Date();

  for (const alert of alerts) {
    const generated = new Date(alert.TimeGenerated);
    const ageMinutes = (now - generated) / 60000;

    // Escalation rules based on severity and age
    let shouldEscalate = false;
    let reason = '';

    switch (alert.Severity) {
      case 4: // Critical
        if (ageMinutes > 15) {
          shouldEscalate = true;
          reason = 'Critical alert unacknowledged for 15+ minutes';
        }
        break;
      case 3: // Error
        if (ageMinutes > 60) {
          shouldEscalate = true;
          reason = 'Error alert unacknowledged for 1+ hour';
        }
        break;
      case 2: // Warning
        if (ageMinutes > 240) {
          shouldEscalate = true;
          reason = 'Warning alert unacknowledged for 4+ hours';
        }
        break;
    }

    if (shouldEscalate) {
      escalations.push({
        alertId: alert.AlertID,
        subject: alert.Subject,
        client: alert.ClientName,
        computer: alert.ComputerName,
        severity: alert.Severity,
        ageMinutes: Math.round(ageMinutes),
        reason
      });
    }
  }

  return escalations;
}

Error Handling

Common Alert API Errors

ErrorStatusCauseResolution
Alert not found404Invalid AlertIDVerify alert exists
Already resolved400Alert already closedCheck current status
Permission denied403No access to alertCheck user permissions
Invalid status400Invalid status transitionFollow lifecycle rules
Ticket creation failed400PSA integration errorCheck ticket board config

Error Response Example

json
{
  "error": {
    "code": "BadRequest",
    "message": "Cannot acknowledge already resolved alert"
  }
}

Safe Alert Resolution

javascript
async function safeResolveAlert(client, alertId, notes) {
  // Get current alert status
  const alert = await client.request(`/Alerts/${alertId}`);

  if (alert.Status === 'Resolved' || alert.Status === 'Cleared') {
    return {
      success: false,
      error: `Alert already ${alert.Status.toLowerCase()}`
    };
  }

  try {
    await client.request(`/Alerts/${alertId}/Resolve`, {
      method: 'POST',
      body: JSON.stringify({ Notes: notes })
    });

    return {
      success: true,
      alertId,
      previousStatus: alert.Status,
      newStatus: 'Resolved'
    };
  } catch (error) {
    return {
      success: false,
      error: error.message
    };
  }
}

Best Practices

  1. Acknowledge promptly - Shows someone is working on it
  2. Add meaningful notes - Document investigation steps
  3. Create tickets for tracking - Long-running issues need tickets
  4. Use bulk operations - Handle related alerts together
  5. Set up escalation rules - Don't let alerts age
  6. Filter by severity - Focus on critical first
  7. Review alert history - Understand recurring patterns
  8. Suppress during maintenance - Avoid alert fatigue
  9. Link to documentation - Reference runbooks in notes
  10. Regular alert review - Audit old/stale alerts

Alert Response Workflow Template

javascript
async function standardAlertResponse(client, alertId) {
  const workflow = {
    steps: [],
    success: true
  };

  try {
    // Step 1: Get alert details
    const alert = await client.request(`/Alerts/${alertId}`);
    workflow.steps.push({
      step: 'Get Alert',
      status: 'success',
      data: {
        subject: alert.Subject,
        severity: alert.Severity,
        computer: alert.ComputerName
      }
    });

    // Step 2: Acknowledge if not already
    if (alert.Status === 'New' || alert.Status === 'Active') {
      await client.request(`/Alerts/${alertId}/Acknowledge`, {
        method: 'POST',
        body: JSON.stringify({ Notes: 'Investigating alert' })
      });
      workflow.steps.push({ step: 'Acknowledge', status: 'success' });
    }

    // Step 3: Create ticket if critical/error
    if (alert.Severity >= 3) {
      const ticket = await client.request(`/Alerts/${alertId}/CreateTicket`, {
        method: 'POST',
        body: JSON.stringify({
          TicketSubject: alert.Subject,
          Priority: alert.Severity === 4 ? 1 : 2,
          BoardID: 1
        })
      });
      workflow.steps.push({
        step: 'Create Ticket',
        status: 'success',
        ticketId: ticket.TicketID
      });
    }

    // Step 4: Check computer status
    const computer = await client.request(`/Computers/${alert.ComputerID}`);
    workflow.steps.push({
      step: 'Check Computer',
      status: 'success',
      computerStatus: computer.Status
    });

  } catch (error) {
    workflow.success = false;
    workflow.steps.push({
      step: 'Error',
      status: 'failed',
      error: error.message
    });
  }

  return workflow;
}

Related Skills