ThreatLocker API Patterns — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited ThreatLocker 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 ThreatLocker MCP server wraps the ThreatLocker Portal API (https://portalapi.g.threatlocker.com/portalapi) and exposes tools across computers, computer groups, approval requests, audit log ("Action Log" in the UI), and organizations. The API has two quirks that surprise people: the auth header does NOT use Bearer, and most "list" endpoints are POSTs against /Entity/EntityGetByParameters with a structured body — not GETs with query strings.
ThreatLocker authenticates with a raw API key — no Bearer prefix:
| Header | Value |
|---|---|
Authorization | <apiKey> (raw, no prefix) |
ManagedClientId | (optional) for legacy partner setups |
organizationId | (optional) tenant to scope this call to |
Common mistake: Sending Authorization: Bearer <key> returns 401. Send the key bare.export THREATLOCKER_API_KEY="your-raw-api-key"
export THREATLOCKER_BASE_URL="https://portalapi.g.threatlocker.com/portalapi"ThreatLocker is built for MSPs and uses organizations as the tenant boundary. Three patterns:
organizationId header. The API usesthe API key's primary organization.
organizationId to a child org ID toscope a single call to that tenant.
childOrganizations: trueon *GetByParameters endpoints to roll up data across all child organizations the API key can see.
For tenant pivots, see the organizations skill.
| Tool | Description |
|---|---|
threatlocker_computers_list | List computers with pagination/filters |
threatlocker_computers_get | Get full details for one computer |
threatlocker_computers_get_checkins | Recent check-in history |
| Tool | Description |
|---|---|
threatlocker_computer_groups_list | Full group list with metadata |
threatlocker_computer_groups_dropdown | Slim list for selection UIs |
| Tool | Description |
|---|---|
threatlocker_approvals_list | List approval requests |
threatlocker_approvals_get | Single approval with full context |
threatlocker_approvals_pending_count | Quick pending-queue size |
threatlocker_approvals_get_permit_application | Application that would be permitted |
| Tool | Description |
|---|---|
threatlocker_audit_search | Search Action Log by time/host/file |
threatlocker_audit_get | Full record for one action |
threatlocker_audit_file_history | All actions for a given file path/hash |
| Tool | Description |
|---|---|
threatlocker_organizations_list_children | All child orgs visible to the key |
threatlocker_organizations_get_auth_key | Auth key for a specific org |
threatlocker_organizations_for_move_computers | Orgs eligible as move targets |
Most list endpoints are POST /Entity/EntityGetByParameters with a JSON body. Request shape:
{
"pageNumber": 1,
"pageSize": 50,
"isAscending": false,
"orderBy": "lastCheckin",
"searchText": "",
"childOrganizations": false
}Response shape:
{
"totalItems": 1284,
"items": [ /* array of entities */ ]
}ThreatLocker uses page numbers, not cursors:
pageNumber: 1, pageSize: 50 (or 100/200).totalPages = ceil(totalItems / pageSize).totalItems rows or reachedthe last page.
Avoid huge pageSize values — large pages can time out. 50–200 is the sweet spot.
orderBy is an entity-specific column name (e.g. lastCheckin,computerName).
isAscending toggles direction.searchText is a substring match across the entity's searchablecolumns (computer name, hostname, OS, etc.).
| Code | Meaning | Fix |
|---|---|---|
| 401 | Bad API key OR Bearer prefix included | Send raw key |
| 403 | Key valid, but no access to requested org | Check organizationId |
| 404 | Wrong endpoint or unknown entity ID | Recheck path |
| 429 | Rate limited | Backoff and retry |
| 500 | Portal API hiccup | Retry, then escalate |
pageSize: 100 and paginate; always check totalItemsbefore assuming you have the full set.
childOrganizations: truerather than looping per-org when the entity supports it.
threatlocker_organizations_list_children —child org lists rarely change within a session.
organizationId explicitly when generating client-facingreports so the source tenant is unambiguous.
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.