Skip to content

Integrations overview

Lockstep VM unifies data from every tool an MSP runs — EDR, RMM, MDM, a vuln scanner, a directory, and tools with no API at all (via CSV/Excel import) — into one per-tenant inventory. Each API integration follows the same lifecycle and the same code shape, so once you understand one you understand them all.

The shared integration lifecycle

Every API integration (SentinelOne, Rapid7, Intune, ConnectWise Automate, Entra ID) is implemented as a FastAPI router plus a background sync task, and every one of them moves through these five stages:

  1. Env seed — on backend startup, app/main.py runs a _seed_*_config coroutine for each source. If the relevant environment variables are set and no config yet exists for the default tenant, it inserts a config document keyed tenant_id: "default". This lets an operator wire credentials once (in the environment) and have the UI ready immediately, with no manual setup.
  2. Adopt-on-read config — each router has a _resolve_config(tenant_id, db) helper. It looks up the tenant’s own config first; if none exists and the tenant is not default, it copies the env-seeded default config into that tenant on first access (an upsert with $setOnInsert). After adoption, per-tenant edits and deletes win — the tenant owns its copy. Config lives in a per-source collection keyed by tenant_id (sentinelone_config, rapid7_config, intune_config, connectwise_automate_config, entra_config).
  3. TestPOST .../test-connection builds a client via _build_client and makes one live API call to validate credentials before you rely on a sync.
  4. SyncPOST .../sync enqueues a background _run_sync(tenant_id, db) task (FastAPI BackgroundTasks). All blocking HTTP runs in a thread-pool executor so the event loop (and login/auth) stays responsive. The task sets sync_status: running, pulls data, upserts it, deletes stale rows (anything whose live ID is no longer returned), and finally records sync_status: success (or error with a message). Poll GET .../sync-status for progress; GET .../stats for summary counts.
  5. Rollup — after writing rows, the sync calls rebuild_applications(tenant_id, db) so installed software from that source rolls up into the shared per-tenant Software inventory alongside every other source.

Because config is per-tenant and clients are rebuilt from stored config on every call, the whole system is multi-tenant with no shared client state.

source tagging into inventory_items

Installed-software rows from every source are written into one shared collection, inventory_items, each row tagged with a source field (sentinelone, rapid7, intune, connectwise_automate, plus csv for imported data). This is what lets:

  • the Software inventory roll up the same app seen by multiple tools,
  • CVE-to-device matching work uniformly regardless of which tool reported the app, and
  • stale cleanup stay scoped — each sync only deletes inventory_items rows for its own source.

Rows carry device_id / device_name, application_name, vendor, version, os_platform and (where known) user_email / user_name, so devices and users reconcile across sources. Sources cross-reference each other by hostname — e.g. SentinelOne and Rapid7 look up matching Intune devices to adopt the Intune device UUID and owner, so a machine is one record, not one per tool.

All sources at a glance

SourceWhat it isAuthWhat it pullsCollections written
SentinelOneEDR / endpoint protectionAPI token + console URLAgents/devices, threats, installed apps, app-risk CVEssentinelone_devices, sentinelone_threats, sentinelone_app_risks, inventory_items (source=sentinelone), vulnerabilities
Rapid7 InsightVMVulnerability scannerAPI key + regionAssets, per-asset vulnerability findings, per-asset softwarerapid7_assets, rapid7_findings, inventory_items (source=rapid7)
Microsoft IntuneMDM / device managementMSAL (cert thumbprint+PEM, or client secret)Managed devices, detected apps, per-device appsintune_devices, intune_apps, inventory_items (source=intune)
ConnectWise Manage (PSA)Ticketing / PSABasic auth (company+public key : private key) + clientIdService tickets, notes, time entries, configurationsconnectwise_tickets, cw_ticket_notes, cw_ticket_cache, cw_ticket_assets, cw_bulk_pull_jobs
ConnectWise AutomateRMMIntegrator token (username/password + optional clientId)Computers, per-computer software — scoped to selected client(s)connectwise_automate_devices, inventory_items (source=connectwise_automate)
Microsoft Entra IDIdentity / directoryMSAL (reuses the Intune app registration)Users (+ aliases), groups, group membershipsentra_users, entra_groups
CSV / Excel providersTools with no APIFile uploadDevices, software and/or users from an uploaded sheetinventory_items (source=csv), csv_users, provider-specific

Two ConnectWise integrations — don’t confuse them

There are two distinct ConnectWise products wired in, and they are separate integrations:

  • ConnectWise Manage (PSA/ticketing) — module connectwise.py, mounted at /api/v1/connectwise. It creates and syncs service tickets (including vulnerability tickets), notes, time entries and configuration links. It uses ConnectWise REST Basic auth (company+publicKey:privateKey plus a clientId header) and reads env vars or a DB config. It does not feed the inventory.
  • ConnectWise Automate (RMM) — module connectwise_automate.py, mounted at /api/v1/connectwise-automate. It pulls computers and installed software into the inventory, and follows the standard integration lifecycle above. See its dedicated page.

Where to go next