---
name: "ConnectWise Automate Alerts"
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.
when_to_use: >-
  When listing active alerts, acknowledging alerts, viewing alert history, and creating tickets
  from alerts. Use when: automate alert, automate notification, alert acknowledgment, alert
  history, alert ticket, monitor alert, labtech alert, or 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

| Source | Description | Example |
|--------|-------------|---------|
| **Monitor** | Generated by monitor threshold | CPU > 90% |
| **Script** | Generated by script execution | Backup failed |
| **Event Log** | Windows Event Log trigger | Security event |
| **System** | Automate system events | Agent offline |
| **Manual** | User-created alerts | Maintenance note |

### Alert Severity Levels

| Level | Value | Description | Response Time |
|-------|-------|-------------|---------------|
| `Information` | 1 | Informational only | Review at convenience |
| `Warning` | 2 | Potential issue | Investigate within hours |
| `Error` | 3 | Failure detected | Respond within SLA |
| `Critical` | 4 | Severe/emergency | Immediate response |

### Alert Lifecycle

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

### Alert Status

| Status | Description |
|--------|-------------|
| `New` | Just generated, unread |
| `Active` | Open, unacknowledged |
| `Acknowledged` | Someone is working on it |
| `Resolved` | Issue fixed, alert closed |
| `Cleared` | Condition auto-cleared |
| `Suppressed` | Temporarily 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

| Error | Status | Cause | Resolution |
|-------|--------|-------|------------|
| Alert not found | 404 | Invalid AlertID | Verify alert exists |
| Already resolved | 400 | Alert already closed | Check current status |
| Permission denied | 403 | No access to alert | Check user permissions |
| Invalid status | 400 | Invalid status transition | Follow lifecycle rules |
| Ticket creation failed | 400 | PSA integration error | Check 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

- [ConnectWise Automate Monitors](../monitors/SKILL.md) - Alert sources
- [ConnectWise Automate Computers](../computers/SKILL.md) - Alert targets
- [ConnectWise Automate Scripts](../scripts/SKILL.md) - Remediation scripts
- [ConnectWise Automate API Patterns](../api-patterns/SKILL.md) - Authentication and pagination
