Skip to content

ConnectWise Automate

ConnectWise Automate is the RMM source. The integration pulls computers and their installed software into the inventory. It follows the shared integration lifecycle.

  • Router: app/api/v1/endpoints/connectwise_automate.py, mounted at /api/v1/connectwise-automate
  • Client: app/services/connectwise_automate_client.py (ConnectWiseAutomateClient)
  • Config collection: connectwise_automate_config (keyed by tenant_id)

Authentication

Automate uses the Integrator token flow. The client posts the username and password (plus an optional clientId header) to the token endpoint, gets a bearer token back, and sends it on subsequent calls:

POST <api_url>/cwa/api/v1/apitoken { "UserName": ..., "Password": ... } (+ optional clientId header)
→ { "AccessToken": ..., "ExpirationDate": ... }
Authorization: Bearer <AccessToken>

The token is cached and refreshed ~60s before expiry; a 401 on any request triggers a single re-auth-and-retry. Config fields (CWAutomateConfig):

FieldMeaning
api_urlAutomate server base URL (API root is <api_url>/cwa/api/v1)
username / passwordIntegrator credentials (password is optional on update so the UI can omit an unchanged secret)
client_idOptional clientId header value
scope_client_ids / scope_client_namesClient scoping — see below
enabledWhether the integration is active

Env seeding (_seed_connectwise_automate_config) reads CW_AUTOMATE_URL, CW_AUTOMATE_USERNAME, CW_AUTOMATE_PASSWORD and CW_AUTOMATE_CLIENT_ID.

Client scoping (important)

An Automate server is typically an MSP server hosting many client companies. Pulling every client’s devices is almost never what you want, so the config carries scope_client_ids — the sync only ingests computers belonging to the selected client(s).

  • Discover clients: GET /api/v1/connectwise-automate/clients calls the client’s get_clients() (Automate /Clients) and returns {id, name} for every company, so the UI can present a picker.
  • Scoped pull: for each configured id, the sync calls get_computers_for_client(client_id), which fetches /Computers with the server-side condition Client.Id=<id> — so filtering happens on the Automate server, not client-side.
  • Empty scope = everything: if no scope_client_ids are set, the sync falls back to get_all_computers() (all clients). The UI warns against this on MSP servers.

Client scoping is only overwritten on save when explicitly provided, so partial config saves don’t wipe it.

What the sync pulls

The background _run_sync:

  1. Computers — scoped as above → upserted into connectwise_automate_devices (device name, OS + normalized os_platform, version, domain, IP, agent version, last contact, last user, and the Automate client name).
  2. Installed software per computer — batched (25 computers per batch), via get_computer_software(computer_id) (/Computers/{id}/Software) → upserted into inventory_items tagged source="connectwise_automate".
DataWritten to
Computersconnectwise_automate_devices
Per-computer installed softwareinventory_items (source=connectwise_automate)

Like Rapid7, software collection uses a circuit breaker: if the /Software sub-resource is unavailable, it stops after 15 consecutive failures instead of hammering every computer. Stale devices and inventory rows are deleted, then rebuild_applications refreshes the Software rollup.

NetBIOS 15-char hostname behavior

ConnectWise Automate reports the 15-character NetBIOS hostname (uppercased), while Intune and SentinelOne report the full name. To link the same machine regardless of source, the device-by-name lookup matches on both the requested name and its upper()[:15] NetBIOS truncation (e.g. LTG-ARamaraju-LTLTG-ARAMARAJU-L), case-insensitively.

Routes

MethodPathPurpose
POST/api/v1/connectwise-automate/configSave credentials + scope (password/scope only overwritten when supplied)
GET/api/v1/connectwise-automate/configGet config (password redacted)
DELETE/api/v1/connectwise-automate/configRemove config + synced data
GET/api/v1/connectwise-automate/clientsList Automate clients (companies) for the scope picker
POST/api/v1/connectwise-automate/test-connectionLive credential test
POST/api/v1/connectwise-automate/syncStart a background sync
GET/api/v1/connectwise-automate/sync-statusPoll sync status
GET/api/v1/connectwise-automate/statsDevice count + app-row count
GET/api/v1/connectwise-automate/devicesList synced computers
GET/api/v1/connectwise-automate/device-by-name/{device_name}Device by hostname (full or NetBIOS match)

See the overview for the lifecycle these routes share.