ConnectWise Automate Client Management
Overview
Clients in ConnectWise Automate represent customer organizations. Each client can have multiple locations (physical sites), and computers belong to specific locations within clients. This skill covers client CRUD operations, location management, client-level settings, and group configurations.
Key Concepts
Client Hierarchy
code
Client (Organization)
├── Location 1 (Physical Site)
│ ├── Computer A
│ └── Computer B
├── Location 2
│ └── Computer C
└── Client Settings
├── EDFs (Custom Fields)
├── Groups
└── Policies
Client Identifiers
| Identifier | Type | Description | Example |
|---|---|---|---|
ClientID | integer | Primary key, auto-incrementing | 100 |
Name | string | Client display name | Acme Corporation |
ExternalID | string | External system reference | CW-12345 |
City | string | Primary city | Chicago |
Location Identifiers
| Identifier | Type | Description | Example |
|---|---|---|---|
LocationID | integer | Primary key | 1 |
Name | string | Location name | Main Office |
ClientID | integer | Parent client | 100 |
Address | string | Street address | 123 Main St |
Field Reference
Client Fields
typescript
interface Client {
// Identifiers
ClientID: number; // Primary key
Name: string; // Display name
ExternalID: string; // External reference
// Contact Information
Address1: string; // Street address line 1
Address2: string; // Street address line 2
City: string; // City
State: string; // State/Province
Zip: string; // Postal code
Country: string; // Country
Phone: string; // Primary phone
Fax: string; // Fax number
Website: string; // Website URL
// Primary Contact
ContactName: string; // Primary contact name
ContactEmail: string; // Primary contact email
ContactPhone: string; // Primary contact phone
// Settings
Comment: string; // Client notes
DefaultRouterAddress: string; // Default gateway
DateAdded: string; // Creation date
// Counts
ComputerCount: number; // Total computers
LocationCount: number; // Total locations
// Extra Data Fields
ExtraData: {
[key: string]: string;
};
}
Location Fields
typescript
interface Location {
// Identifiers
LocationID: number; // Primary key
ClientID: number; // Parent client
Name: string; // Location name
// Address
Address1: string;
Address2: string;
City: string;
State: string;
Zip: string;
Country: string;
Phone: string;
// Network
Router: string; // Default router IP
NetworkProbe: number; // Network probe computer ID
// Settings
Comment: string; // Location notes
DateAdded: string; // Creation date
// Counts
ComputerCount: number; // Computers at this location
// Extra Data Fields
ExtraData: {
[key: string]: string;
};
}
Group Fields
typescript
interface Group {
GroupID: number; // Primary key
Name: string; // Group name
FullPath: string; // Full hierarchy path
ParentID: number; // Parent group ID
ClientID: number; // 0 for global groups
Template: number; // Template group ID
AutoJoinScript: number; // Auto-join script ID
// Limits
LimitToParent: number; // Limit to parent group
NetworkProbe: number; // Network probe
// Scripts
Scripts: number[]; // Associated script IDs
Monitors: number[]; // Associated monitor IDs
}
API Patterns
List All Clients
http
GET /cwa/api/v1/Clients?pageSize=250
Authorization: Bearer {token}
Response:
json
[
{
"ClientID": 100,
"Name": "Acme Corporation",
"City": "Chicago",
"State": "IL",
"Phone": "(312) 555-1234",
"ContactName": "John Smith",
"ContactEmail": "jsmith@acme.com",
"ComputerCount": 45,
"LocationCount": 3,
"DateAdded": "2020-01-15T08:00:00Z"
}
]
Get Single Client
http
GET /cwa/api/v1/Clients/{clientID}
Authorization: Bearer {token}
Create Client
http
POST /cwa/api/v1/Clients
Authorization: Bearer {token}
Content-Type: application/json
{
"Name": "New Client Inc",
"Address1": "456 Business Ave",
"City": "New York",
"State": "NY",
"Zip": "10001",
"Phone": "(212) 555-9876",
"ContactName": "Jane Doe",
"ContactEmail": "jdoe@newclient.com"
}
Response:
json
{
"ClientID": 101,
"Name": "New Client Inc",
"City": "New York",
"DateAdded": "2024-02-15T10:30:00Z"
}
Update Client
http
PATCH /cwa/api/v1/Clients/{clientID}
Authorization: Bearer {token}
Content-Type: application/json
{
"Phone": "(212) 555-1111",
"ContactEmail": "newcontact@newclient.com"
}
Delete Client
http
DELETE /cwa/api/v1/Clients/{clientID}
Authorization: Bearer {token}
Note: Deleting a client will also delete all associated locations and unassign computers.
List Client Locations
http
GET /cwa/api/v1/Clients/{clientID}/Locations
Authorization: Bearer {token}
Response:
json
[
{
"LocationID": 1,
"ClientID": 100,
"Name": "Main Office",
"City": "Chicago",
"State": "IL",
"ComputerCount": 30
},
{
"LocationID": 2,
"ClientID": 100,
"Name": "Remote Office",
"City": "Detroit",
"State": "MI",
"ComputerCount": 15
}
]
Create Location
http
POST /cwa/api/v1/Clients/{clientID}/Locations
Authorization: Bearer {token}
Content-Type: application/json
{
"Name": "New Branch Office",
"Address1": "789 Branch St",
"City": "Detroit",
"State": "MI",
"Zip": "48201"
}
Get Client Computers
http
GET /cwa/api/v1/Clients/{clientID}/Computers?pageSize=250
Authorization: Bearer {token}
Get Client Groups
http
GET /cwa/api/v1/Clients/{clientID}/Groups
Authorization: Bearer {token}
Get Client EDFs
http
GET /cwa/api/v1/Clients/{clientID}/ExtraDataFields
Authorization: Bearer {token}
Response:
json
[
{
"EDFID": 1,
"Name": "Contract Type",
"Value": "Managed Services",
"Type": "Text"
},
{
"EDFID": 2,
"Name": "SLA Level",
"Value": "Premium",
"Type": "Dropdown"
}
]
Update Client EDF
http
PUT /cwa/api/v1/Clients/{clientID}/ExtraDataFields/{edfID}
Authorization: Bearer {token}
Content-Type: application/json
{
"Value": "Gold"
}
Workflows
Client Lookup by Name
javascript
async function findClientByName(client, name) {
const clients = await client.request(
`/Clients?condition=Name contains '${name}'`
);
if (clients.length === 0) {
return { found: false, suggestions: [] };
}
if (clients.length === 1) {
return { found: true, client: clients[0] };
}
return {
found: false,
ambiguous: true,
suggestions: clients.map(c => ({
name: c.Name,
id: c.ClientID,
city: c.City,
computerCount: c.ComputerCount
}))
};
}
Create Client with Default Location
javascript
async function createClientWithLocation(apiClient, clientData, locationName = 'Main Office') {
// Create the client
const newClient = await apiClient.request('/Clients', {
method: 'POST',
body: JSON.stringify(clientData)
});
// Create default location
const location = await apiClient.request(
`/Clients/${newClient.ClientID}/Locations`,
{
method: 'POST',
body: JSON.stringify({
Name: locationName,
Address1: clientData.Address1,
City: clientData.City,
State: clientData.State,
Zip: clientData.Zip
})
}
);
return {
client: newClient,
location: location
};
}
Bulk Client Report
javascript
async function generateClientReport(apiClient) {
const clients = await apiClient.request('/Clients?pageSize=500');
const report = [];
for (const client of clients) {
const locations = await apiClient.request(
`/Clients/${client.ClientID}/Locations`
);
report.push({
name: client.Name,
id: client.ClientID,
contact: client.ContactName,
email: client.ContactEmail,
computers: client.ComputerCount,
locations: locations.map(l => l.Name)
});
// Respect rate limits
await sleep(100);
}
return report;
}
Client Health Dashboard
javascript
async function getClientHealth(apiClient, clientId) {
const client = await apiClient.request(`/Clients/${clientId}`);
const computers = await apiClient.request(
`/Clients/${clientId}/Computers?pageSize=500`
);
const online = computers.filter(c => c.Status === 'Online').length;
const offline = computers.filter(c => c.Status === 'Offline').length;
return {
client: client.Name,
totalComputers: computers.length,
online,
offline,
healthPercentage: Math.round((online / computers.length) * 100),
offlineComputers: computers
.filter(c => c.Status === 'Offline')
.map(c => ({
name: c.Name,
lastContact: c.LastContact
}))
};
}
Update Client EDFs
javascript
async function updateClientEDFs(apiClient, clientId, edfUpdates) {
const results = [];
// Get existing EDFs
const edfs = await apiClient.request(
`/Clients/${clientId}/ExtraDataFields`
);
for (const [name, value] of Object.entries(edfUpdates)) {
const edf = edfs.find(e => e.Name === name);
if (edf) {
await apiClient.request(
`/Clients/${clientId}/ExtraDataFields/${edf.EDFID}`,
{
method: 'PUT',
body: JSON.stringify({ Value: value })
}
);
results.push({ name, status: 'updated', value });
} else {
results.push({ name, status: 'not_found' });
}
}
return results;
}
Error Handling
Common Client API Errors
| Error | Status | Cause | Resolution |
|---|---|---|---|
| Client not found | 404 | Invalid ClientID | Verify client exists |
| Duplicate name | 400 | Client name exists | Use unique name |
| Invalid EDF | 400 | EDF doesn't exist | Check EDF configuration |
| Permission denied | 403 | Insufficient rights | Check user permissions |
| Has computers | 400 | Client has assigned computers | Remove computers first |
Error Response Example
json
{
"error": {
"code": "BadRequest",
"message": "Cannot delete client with assigned computers"
}
}
Safe Client Deletion
javascript
async function safeDeleteClient(apiClient, clientId, options = {}) {
const { force = false, moveComputersTo = null } = options;
// Check for computers
const computers = await apiClient.request(
`/Clients/${clientId}/Computers`
);
if (computers.length > 0) {
if (!force && !moveComputersTo) {
return {
success: false,
error: `Client has ${computers.length} computer(s)`,
computers: computers.map(c => c.Name)
};
}
if (moveComputersTo) {
// Move computers to another client
for (const computer of computers) {
await apiClient.request(`/Computers/${computer.ComputerID}`, {
method: 'PATCH',
body: JSON.stringify({
ClientID: moveComputersTo.clientId,
LocationID: moveComputersTo.locationId
})
});
await sleep(100);
}
}
}
// Delete the client
await apiClient.request(`/Clients/${clientId}`, {
method: 'DELETE'
});
return { success: true };
}
Best Practices
- •Use ExternalID for integrations - Link to PSA/CRM systems
- •Standardize naming conventions - Consistent client names
- •Create locations for each site - Better organization
- •Use EDFs for business data - Contract type, SLA level, etc.
- •Maintain contact information - Keep primary contacts updated
- •Group by client type - MSP vs internal, etc.
- •Regular client audits - Review inactive clients
- •Document client-specific settings - Notes in Comment field
- •Use groups for policies - Apply settings at group level
- •Plan location structure - Consider VPN, network segments
Client Onboarding Workflow
javascript
async function onboardNewClient(apiClient, clientInfo) {
const results = {
steps: [],
success: true
};
try {
// Step 1: Create client
const client = await apiClient.request('/Clients', {
method: 'POST',
body: JSON.stringify({
Name: clientInfo.name,
Address1: clientInfo.address,
City: clientInfo.city,
State: clientInfo.state,
Zip: clientInfo.zip,
Phone: clientInfo.phone,
ContactName: clientInfo.contactName,
ContactEmail: clientInfo.contactEmail,
ExternalID: clientInfo.externalId
})
});
results.steps.push({ step: 'Create Client', status: 'success', id: client.ClientID });
// Step 2: Create primary location
const location = await apiClient.request(
`/Clients/${client.ClientID}/Locations`,
{
method: 'POST',
body: JSON.stringify({
Name: 'Main Office',
Address1: clientInfo.address,
City: clientInfo.city,
State: clientInfo.state,
Zip: clientInfo.zip
})
}
);
results.steps.push({ step: 'Create Location', status: 'success', id: location.LocationID });
// Step 3: Set EDFs
if (clientInfo.edfs) {
await updateClientEDFs(apiClient, client.ClientID, clientInfo.edfs);
results.steps.push({ step: 'Set EDFs', status: 'success' });
}
// Step 4: Add to groups (if specified)
if (clientInfo.groups) {
for (const groupId of clientInfo.groups) {
await apiClient.request(`/Groups/${groupId}/Clients`, {
method: 'POST',
body: JSON.stringify({ ClientID: client.ClientID })
});
}
results.steps.push({ step: 'Add to Groups', status: 'success' });
}
results.clientId = client.ClientID;
results.locationId = location.LocationID;
} catch (error) {
results.success = false;
results.error = error.message;
}
return results;
}
Related Skills
- •ConnectWise Automate Computers - Computers within clients
- •ConnectWise Automate Scripts - Client-scoped scripts
- •ConnectWise Automate Monitors - Client monitoring
- •ConnectWise Automate Alerts - Client alerts
- •ConnectWise Automate API Patterns - Authentication and pagination