Kaseya BMS API Patterns — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited Kaseya BMS 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-bms-mcp) and SDK (@wyre-technology/node-kaseya-bms) are in development. This skill is reference documentation.
Kaseya BMS exposes a REST v2 API at /api/ on every BMS tenant. Reference: <https://help.bms.kaseya.com/help/Content/BMS%20API/bms-api-v2-bms-rest-apis.html>
Each MSP has a private tenant — there is no shared regional endpoint:
https://<tenant>.bms.kaseya.com/apiThe tenant subdomain is a credential field; never hard-code.
Two supported flows.
Authorization: Bearer <api_token>
X-Tenant: <tenantSubdomain>Tokens are long-lived; revoke from the same UI.
On Kaseya One tenants, mint a token via https://one.kaseya.com/oauth/token with scope=bms.api, then call BMS endpoints with Authorization: Bearer <kaseya_one_jwt>. The JWT carries the tenant claim — no X-Tenant header needed.
OData-style:
| Param | Default | Max | Notes |
|---|---|---|---|
$top | 50 | 500 | Page size |
$skip | 0 | — | Records to skip |
$filter | none | — | OData filter (e.g. Status eq 'Open') |
$orderby | none | — | e.g. CreatedDate desc |
Responses include TotalRecords for total count; loop with $skip += $top until $skip >= TotalRecords.
| Domain | Endpoint | Notes |
|---|---|---|
| Tickets | GET /api/v2/service/tickets | Filter via $filter |
| Single ticket | GET /api/v2/service/tickets/{id} | Includes notes if ?includeNotes=true |
| Create ticket | POST /api/v2/service/tickets | |
| Add ticket note | POST /api/v2/service/tickets/{id}/notes | |
| Time entries | GET /api/v2/service/timeentries | Per-user/ticket |
| Accounts | GET /api/v2/finance/accounts | Clients |
| Contacts | GET /api/v2/crm/contacts | |
| Contracts | GET /api/v2/finance/contracts | |
| Service catalog | GET /api/v2/service/catalog | |
| KB articles | GET /api/v2/service/knowledgebase |
{
"Result": [ /* records */ ],
"TotalRecords": 1234,
"ResponseCode": 0,
"Status": "Ok"
}ResponseCode != 0 means application-level error even with HTTP 200; surface Error field to the user.
BMS throttles at 300 req/min per tenant under default policy. HTTP 429 includes Retry-After. Sustained abuse can trigger temporary tenant-level lockouts — back off aggressively.
| HTTP | Meaning | Action |
|---|---|---|
| 200 | OK (check ResponseCode) | |
| 400 | Bad filter / missing required field | Validate inputs |
| 401 | Bad token or expired | Re-issue token; check X-Tenant |
| 403 | Token lacks scope on this resource | Surface; verify role |
| 404 | Endpoint or record missing | Confirm tenant subdomain |
| 429 | Rate limited | Back off per Retry-After |
| 500-503 | Transient | Exponential backoff |
X-Tenant is required. With Kaseya One SSO, the JWT carries the tenant claim and X-Tenant is ignored (or rejected on some versions). Pick one; don't send both.CreatedDate, Status). Common 400s come from camelCase (createdDate).Status. Setting an invalid transition returns 400 with a generic message; check the workflow definition before bulk updates.AccountID vs AccountId). Defensive parsing pays off.When the build-out lands, expect domain skills under this plugin for: tickets, accounts, contracts, time-entries, knowledge-base.
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.