Management API reference

For developers and automation. You do not need this to deploy or run Highwind Fleet.

API reference

All routes are under /api/v1 unless shown. JSON is camelCase; unknown members are rejected (400 HW-REQUEST-006). Errors return { errorCode, message, correlationId, timestampUtc }. Request bodies are limited to 256 KB, except the App Control policy upload (policy XML up to 8 MiB).

409 when not decided, stale, or nothing can be built
Method & routeRolePurpose
GET /health/liveanonymousProcess is up
GET /health/readyOperatorAuth and database working. Must be 401 without a token
POST /agents/registerAgentRegister device, identity and capabilities
POST /agents/heartbeatAgentLiveness; increasing sequence (409 on replay)
POST /agents/messages/pollAgentLease pending requests
POST /agents/messages/resultAgentReturn a result
POST /fleet/enrollment/redeemAgentSpend an enrollment secret
GET /fleet/healthOperatorFleet liveness summary
GET /fleet/capabilitiesOperatorWhat devices can answer
GET /fleet/devices, /fleet/devices/{id}OperatorInventory; filters status, health, enrolled, name, tag, skip, take, after (the page’s nextCursor, for the name sorts; faster than skip on a large fleet); sort=name | name-desc | seen | seen-asc | version | tag | registered. tag matches the device tag, org unit or environment
POST /fleet/devices/{id}/approve | disable | reinstate | revokeAdmin, or Manage devices on that device’s tagLifecycle; reason required for disable/revoke; revoke is permanent
POST /fleet/requests, GET /fleet/requests/{id}OperatorOne operation on one device (202, then poll status)
POST /fleet/queries, GET /fleet/queries/{id}?includeResults=true, POST /fleet/queries/{id}/cancelOperatorFan one operation out: AllCapableAgents, ExplicitAgents (max 100) or TaggedAgents with targetTag. savedQueryId runs a saved query; parameters (ad-hoc custom queries) needs Admin or Custom queries
GET /fleet/queries?take=50OperatorQuery history, newest first, with who ran each one (max 200)
GET | POST /fleet/saved-queries, PUT | DELETE …/{id}OperatorSaved queries. An operator changes only their own; custom queries (GetCustomCheckResults) need Admin or the Custom queries permission. 400 returns the validation reason
GET /fleet/posture/baseline, GET /fleet/posture/baselinesOperatorThe default baseline; or the default, every tag baseline and the tag values devices report
PUT /fleet/posture/baseline, DELETE /fleet/posture/baseline?tag=Admin or BaselinesReplace a baseline { tag?, version, settings: [{ key, expected, severity }] } (no tag = the default). Send the version you read (0 to create a tag baseline); a stale version is 400. At most 100 tag baselines
GET /fleet/license, GET /fleet/license/featuresOperatorLicense status (state, customer, dates, features, limits, binding, installation ID) and the feature catalog with what is included. Work in every license state
POST /fleet/license/preview, PUT /fleet/licenseAdmin{ fileName, contentBase64, confirmSha256? }: check a .hwlic file without saving; import it (confirmSha256 must be the previewed SHA-256). 400 with the reason, 409 on a concurrent change
POST /fleet/license/validate, GET /fleet/license/summary, GET /fleet/license/historyAdminVerify now; plain-text summary for support; every imported license. All audited
GET /fleet/scopes/meOperatorThe caller’s own permissions, device scope and the tags they can see; reach lists every permission with where it applies (Check my permissions)
GET /fleet/scopes/directory?q=AdminEntra users and security groups whose name matches (needs Graph User.ReadBasic.All and GroupMember.Read.All on the API’s identity)
GET | PUT /fleet/scopes, DELETE …/{id}, PUT …/settingsAdminAccess assignments (Entra group or user object id → tags (empty = every device) and permissions), and whether an unassigned operator sees no devices
GET /fleet/posture?after=, GET /fleet/posture/{id}OperatorLatest answer to each posture question per device (audited as PostureRead)
GET /fleet/posture/trend?scope=&days=30OperatorDaily snapshot numbers, oldest first, for every device (no scope; needs every-device access) or one tag; also lists the tags with history. days 7–400, default 90
POST /fleet/posture/snapshotAdminRetake today’s snapshot (202; the worker takes it within about a minute)
GET | POST /fleet/posture/views, DELETE …/{id}OperatorSaved Posture views { name, report, view, tag, query, issue, area, shared }. Lists your own and shared views
GET /fleet/posture/exportsOperatorExport files you may download (and, for administrators, the schedules)
POST /fleet/posture/exports, PUT | DELETE …/{id}, POST …/{id}/runAdminSchedules { name, report: devices | baseline-devices | issues | baseline | trend, tag?, view?, frequency: Daily | Weekly, dayOfWeek, hourUtc, enabled }. run = produce one now (202)
GET /fleet/posture/exports/files/{fileId}Operator (file’s tag in scope)Download the CSV; X-Content-SHA256 is the hash of the body
POST /fleet/posture/refresh, POST /fleet/posture/{id}/refreshOperator with Run queriesAsk every active device, or one, to refresh posture at its next check-in (202). 409 for a device that is not active
POST /fleet/analysis/egress-grantsOperatorAuthorize and audit sending a query bundle to an AI model. Requires tenant AllowModelEgress; bundleHash is a base64 SHA-256
POST | GET /fleet/enrollment-tokens, POST …/{id}/revokeAdmin, or Mint secrets (POST) / Enrollment (list, revoke)Mint (secret shown once; a justification of 10–500 characters is required and recorded with who minted it), list, revoke
POST | GET /fleet/certificate-authorities, POST …/{id}/disableAdminTrust anchors for client-certificate devices
POST /fleet/device-identity/challenge, …/attestanonymous, rate-limitedAn Entra joined or hybrid joined device proves itself and receives its certificate. 404 HW-IDENT-001 when device identity is off
POST /fleet/device-identity/renewAgent (signed request)Renew the device certificate; the agent does this at half-life
GET | PUT /fleet/device-identity/settings, GET | POST …/authorityAdmin or Enrollment (POST authority: Admin only)Who may attest and the approval rule; the tenant’s device CA (POST creates it and proves the Key Vault key works)
GET /fleet/devices/{id}/identityOperatorHow the device proved itself, its key protection and its current certificate
GET /fleet/app-control/policies, …/{policyId}/versions, …/versions/{n}/contentOperatorBrowse and download App Control policy versions
POST /fleet/app-control/policiesAdminUpload { xml, friendlyName?, notes? }. 201 new, 200 duplicate, 400 invalid, 409 conflict
GET /fleet/activity, GET /fleet/activity/exportAdminActivity log newest first (or CSV, up to 100,000 rows). Filters category=people|devices|service, actor, eventType, outcome=allowed|refused, device, from, to (yyyy-MM-dd UTC), after, limit (1–200)
GET | PUT /fleet/app-control/management/settings, POST …/rebuildOperator (GET); Admin (PUT, rebuild)Module mode, capability switches and separation of duties { mode, capabilities, separationOfDuties? }. Modes above Recommend, or capabilities not built yet (Intune, generation, deployment, validation): 403 HW-APPCTL-001
GET /fleet/app-control/management/overview, …/policies/{policyId}OperatorFleet inventory and one policy’s versions and rollout by group, over the caller’s devices
GET /fleet/app-control/management/devices, …/devices/{agentId}OperatorDevices with findings; filters finding (a finding, any or not-reported), group, policy, name, after, limit; one device with expectations and peer gaps
GET | PUT | DELETE /fleet/app-control/management/expected?scope=Operator (GET); Admin or BaselinesExpected policy sets { scope, flagUnexpected, strategy: unspecified|allowList|denyList, policies: [{ policyId, name?, minimumVersion?, required: active|activeAndEnforcing|present|absent }] }; absent = must not be present (minimum version ignored)
GET /fleet/app-control/management/fingerprints, …/fingerprints/{fingerprint}, …/fingerprints/compare?from=&to=OperatorPolicy sets with device counts and fragmented groups; one set’s content, breakdowns and explanation against each expected set; differences between two sets. 403 when no device in scope carries or carried it
GET /fleet/app-control/management/alerts?open=&state=&type=&severity=&set=&after=, …/alerts/{id}; POST …/alerts/{id}/actionsOperator (scoped); actions: Admin or BaselinesStateful App Control alerts with history and deliveries. Actions { action, note?, assignee?, dueAtUtc?, suppressUntilUtc?, concurrencyStamp }; 409 when the stamp is stale or the transition is not allowed
GET /fleet/app-control/management/notifications, PUT | DELETE …/notifications/subscriptions, POST …/notifications/test, POST …/notifications/deliveries/{id}/retryAdminConfigured channels (name, kind, host only), subscriptions, recent deliveries; test message; retry a given-up delivery
GET /fleet/app-control/management/applications?search=&evidence=blocked|audited|installed&sort=blocked|audited|installed|recent|name&offset=&limit=, …/applications/{id}Unrestricted operator, a Board permission, or AdminNormalised application catalog (one row per application, never per hash) and one application with versions, install sources, events, devices (caller’s scope), groups and reviews
POST /fleet/app-control/management/applications/{id}/mergeAdmin{ intoApplicationId?, reason }: merge into another record, or separate with no target. Reason required; history kept
GET | POST /fleet/app-control/management/importsAdmin (Advanced hunting on)Recent imports; import an Advanced Hunting CSV { fileName, csv } (8 MB, 50,000 rows). Returns rows, accepted, duplicates, rejected and warnings
GET /fleet/app-control/management/reviews?open=&due=&state=&search=&offset=&limit=, …/reviews/{id}As for applicationsBoard reviews with counts by state and the caller’s Board permissions; one review with history and the actions open to the caller
POST /fleet/app-control/management/reviews, POST …/reviews/{id}/actionsThe action’s Board permissionOpen { applicationId, text? }; act { action, text?, person?, outcome?, conditions?, expiresAtUtc?, recertifyAtUtc?, classification?, concurrencyStamp }. 403 with the reason when separation of duties refuses; 409 when the stamp is stale or the action is not available
GET /fleet/app-control/management/reviews/{id}/rulesAny App Control governance permission; mode Recommend + Rule recommendationsRule candidates and proposals with coverage, breadth and narrow risk, target policy, conflicts, known gaps, deny impact, notes and the caller’s available actions
POST …/reviews/{id}/rules/builder/previewRule author (not the decision maker){ main?, fallback?, overrides: [{ fileKey, level }] }. Read only: the rule builder’s steps, coverage, files with their rule, the policy XML and the matching New-CIPolicy command
POST …/reviews/{id}/rules/builderRule author (not the decision maker)Same body + concurrencyStamp (the review’s). Saves the built rule as a candidate and returns the rules with createdProposalId. 409 when stale or a rule is already proposed or approved
POST /fleet/app-control/management/policy-packages/{id}/deleteRule author or Rule approver (approved package: Rule approver){ note?, resetApplications: [applicationId], concurrencyStamp }. Deletes the version and retires the approved rule of each listed application, sending its review back to Needs rule. 409 when stale or the package is in a deployment plan
POST /fleet/app-control/management/rule-proposals/{id}/actions
GET /fleet/app-control/management/rule-proposals/{id}/impactAny App Control governance permission; mode Recommend + Rule recommendations + Policy simulationThe proposal, its newest simulation (latest: figures tagged Observed/Projected/Unknown, conflicts, critical components, installers, other applications, group and build breakdown, devices, missing/stale/not-evaluated data), current, earlier simulations, canSimulate, requiredForApproval. Devices outside your scope are left out of the lists
POST …/rule-proposals/{id}/impactRule author or Rule approverNo body. Simulates a candidate, proposed or approved rule against recorded events and reported policies, stores it and records it in the review history. Changes no policy. 409 for a rejected, withdrawn or superseded rule; a repeat within 30 s returns the stored result
GET /fleet/app-control/management/applications/{id}/filesAs for applications; capability Application file scansThe application’s files from device scans (path, kind, version, signer, Authenticode SHA-256, issuer TBS hash, devices that block or would block), signers, latest scan per device, scanDevices (devices you may ask: agentId, deviceName, observedAtUtc; at most 200), rule kinds in the tenant’s order and the suggested plan. Scans of devices outside your scope are left out
POST …/applications/{id}/files/scanRun queries on each device asked{ agentIds? } (at most 5; default the 3 most recently seen with the application and FleetAppControlFileScanEnabled; pick from scanDevices of the GET). 409 when no device can answer or all are already scanning it
GET /fleet/app-control/management/policy-packages, …/policy-packages/{id}Any App Control governance permission; mode Generate + Policy package generationOne line per policy that can be built: each application’s own policy, merged policies and the deny base (lineKey, title, applications, approved rules, not yet packaged, no longer approved, newest version, newest approved version) and the newest 200 packages; one package with its XML, errors, warnings, changes and rules
POST /fleet/app-control/management/policy-packagesRule author{ kind: "supplemental"|"denyBase", basePolicyId?, lineKey?, applicationIds?, name?, auditMode? } (auditMode defaults to true; deny base only). lineKey rebuilds a line; otherwise one application in applicationIds builds its own policy, two or more (same base, at most 100) build a new merged policy, none builds every approved rule of the base in one policy. Each version holds every approved rule of its line. 409 when nothing is approved or nothing changed
POST …/policy-packages/{id}/actionsRule approver (not the builder){ action: "approve"|"reject", note?, concurrencyStamp }. Reject needs note. Approve needs a validated package whose rules are all still approved and simulated
GET …/policy-packages/{id}/export, …/manifestAny App Control governance permissionApproved packages only (409 otherwise). XML as application/xml with X-Content-SHA256; manifest JSON with IDs, version, SHA-256, rules, approver and compile steps. Audited
GET /fleet/app-control/management/programAny App Control governance permission (or Admin); Application Control Board onCounts (applications, approved, denied, exceptions, in review, not reviewed) and the Needs attention tasks: title, detail, count, link, who acts and whether you can
GET /fleet/app-control/management/registerAny App Control governance permission (or Admin)Decision register CSV (UTF-8 BOM, one row per application, max 50,000, formula-safe, no-store, export rate limit). Every download and refusal audited as AppControlRegisterExported
GET /fleet/app-control/management/trend?scope=&days=, …/convergenceOperator (whole-fleet trend: Admin or unrestricted)Daily App Control points (7–400 days); convergence per expected set. /devices also takes state, fingerprint, oscillating, set (set=(default) for the default set)

Rate limits are per tenant and caller: 600 requests/minute overall, with tighter per-route limits on heartbeats (20/min), request submission (60), query submission (10), egress grants (20), enrollment (10) and export downloads (20). The anonymous device-identity routes allow 20 requests a minute per source address. Over the limit returns 429.