Kaseya VSA API Patterns — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited Kaseya VSA API Patterns (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
The MCP server (kaseya-vsa-mcp) and SDK (@wyre-technology/node-kaseya-vsa) are in development. This skill is reference documentation for the API surface and authentication flow; the implementation will follow the patterns described here.
Kaseya VSA exposes a REST API at /api/v1.0/ on every VSA tenant. Modern VSA 10 / VSA X tenants additionally support Kaseya One SSO bearer tokens issued by https://one.kaseya.com, sharing identity with Autotask, BMS, IT Glue (partial), and Datto EDR.
Reference: <https://help.vsa10.kaseya.com/>
VSA uses a two-step token exchange for API key auth:
GET /api/v1.0/auth with HTTP basic-auth-style headers built from a SHA-256/SHA-1 hash of the user password + a per-request random nonce.Authorization: Bearer <token> on subsequent calls. Token TTL is short (default 15 minutes); the server returns Token-Expires-In in the response.For Kaseya One SSO tenants, skip the password-hash dance and exchange a Kaseya One JWT for a VSA session token instead.
The full Authorization string is six comma-separated fields. Both the "covered" hashes (raw + nonce, re-hashed) and the raw hashes must be sent — omitting rpass2/rpass1 silently 401s every login. Source: <https://help.vsa9.kaseya.com/help/Content/Modules/rest-api/37334.htm>
RawSHA256 = SHA-256(password + username)
RawSHA1 = SHA-1(password + username)
CoveredSHA256 = SHA-256(RawSHA256 + RandomString)
CoveredSHA1 = SHA-1(RawSHA1 + RandomString)
Authorization: Basic user=<username>,
pass2=<CoveredSHA256>,
pass1=<CoveredSHA1>,
rpass2=<RawSHA256>,
rpass1=<RawSHA1>,
rand2=<RandomString>Capture the returned Token field, then send Authorization: Bearer <Token> on every subsequent call.
POST https://one.kaseya.com/oauth/token
grant_type=client_credentials
client_id=<integration client id>
client_secret=<secret>
scope=vsa.api
→ access_token (JWT)
GET https://<tenant>/api/v1.0/auth/sso
Authorization: Bearer <access_token>
→ VSA session token usable as Bearer on subsequent /api/v1.0 callsEach MSP has a private VSA tenant URL — there is no shared regional endpoint. Examples:
https://vsa.exampleMSP.com/api/v1.0https://kaseya.exampleMSP.com/api/v1.0The MCP server takes the tenant URL as a credential field; never hard-code.
VSA uses OData-style $skip + $top (not cursor-based). Every list endpoint accepts:
| Parameter | Default | Max | Description |
|---|---|---|---|
$top | 100 | 1000 | Page size |
$skip | 0 | — | Records to skip |
$filter | none | — | OData filter expression |
$orderby | none | — | Sort, e.g. MachineName asc |
Pagination loop:
loop:
GET /api/v1.0/assetmgmt/agents?$top=1000&$skip=<offset>
read response.Result (the array of records)
read response.TotalRecords (total count)
offset += 1000
if offset >= TotalRecords: stopAll VSA responses use this shape:
{
"Result": [ /* records */ ] or { /* single record */ },
"ResponseCode": 0,
"TotalRecords": 1234,
"Code": "0",
"Status": "Ok",
"Error": null
}A ResponseCode != 0 or non-null Error indicates failure even with HTTP 200. Always check both the HTTP status and Result.ResponseCode / Result.Error.
| Domain | Endpoint | Notes |
|---|---|---|
| Agents | GET /assetmgmt/agents | Returns endpoints with org/group context |
| Agent details | GET /assetmgmt/agents/{agentId} | Hardware, OS, network |
| Software audit | GET /assetmgmt/audit/{agentId}/software | Installed apps |
| Patch status | GET /assetmgmt/patch/status/{agentId} | Pending/installed patches |
| Patch deploy | POST /assetmgmt/patch/{agentId}/deploypatchnow | Force a deploy now |
| Procedures (scripts) | GET /automation/agentprocs/{agentId} | Available procedures |
| Run procedure | POST /automation/agentprocs/{agentId}/{procId}/runnow | Execute |
| Tickets (SD) | GET /servicedesk/tickets | If Service Desk enabled |
| Alarms | GET /assetmgmt/alarms | Active monitor alarms |
| Organizations | GET /system/orgs | Tenant organizations |
| Machine groups | GET /system/machinegroups | Hierarchy under an org |
Kaseya does not publish hard rate limits but throttles aggressively under load. Defensive defaults:
Retry-After (seconds)| HTTP | VSA ResponseCode | Meaning | Action |
|---|---|---|---|
| 200 | 0 | Success | Continue |
| 200 | non-zero | Application error in Error field | Surface message; do not retry |
| 401 | — | Token expired | Re-auth, retry once |
| 403 | — | User lacks scope on this resource | Surface; do not retry |
| 404 | — | Endpoint or record missing | Surface; check tenant URL |
| 429 | — | Throttled | Back off per Retry-After |
| 500-503 | — | Transient | Exponential backoff, max 3 retries |
staticIp is for inbound; outbound uses a different IP.)/api/v1.0/agents → /api/v1.0/agents/ (301), which strips the Authorization header. Normalize all paths to end with /.When the build-out lands, expect domain-specific skills under this plugin for: agents, patches, procedures, tickets, alarms, organizations.
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.