{"openapi":"3.1.0","info":{"title":"Bastion AI API","version":"1.0.0","description":"Read-only access to your Microsoft 365 / Entra security posture: findings plus the correlated inventory (users, devices, apps, domains) and the device↔vulnerability graph. Authenticate with an API key created in the console under Settings."},"servers":[{"url":"https://api.bastionsecurity.dev/api/v1"}],"security":[{"bearerAuth":[]}],"tags":[{"name":"Findings"},{"name":"Inventory"},{"name":"Vulnerabilities"},{"name":"Compliance"}],"paths":{"/findings":{"get":{"summary":"List findings","operationId":"list_findings","tags":["Findings"],"parameters":[{"name":"severity","in":"query","schema":{"type":"string"},"description":"Comma-separated: Critical,High,Medium,Low"},{"name":"status","in":"query","schema":{"type":"string"},"description":"Comma-separated: open,approved,snoozed,fixed,resolved. Default excludes resolved."},{"name":"control","in":"query","schema":{"type":"string"},"description":"Control-code prefix, e.g. ROLE-GA or MFA"},{"name":"category","in":"query","schema":{"type":"string"},"description":"identity | application | endpoint | email | cloud"},{"name":"min_severity","in":"query","schema":{"type":"string","enum":["Critical","High","Medium","Low"]},"description":"At-or-above this severity"},{"name":"limit","in":"query","schema":{"type":"integer","default":500,"maximum":1000},"description":"Page size (max 1000)"},{"name":"offset","in":"query","schema":{"type":"integer","default":0},"description":"Items to skip"},{"name":"updated_after","in":"query","schema":{"type":"string","format":"date-time"},"description":"Only items whose last_seen is after this ISO timestamp (incremental sync)"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"workspace":{"type":"string"},"generated_at":{"type":"string","format":"date-time"},"total":{"type":"integer","description":"Total matching items (before pagination)"},"count":{"type":"integer","description":"Items in this page"},"limit":{"type":"integer"},"offset":{"type":"integer"},"has_more":{"type":"boolean"},"findings":{"type":"array","items":{"$ref":"#/components/schemas/Finding"}}},"required":["workspace","generated_at","total","count","findings"]}}}},"401":{"description":"Missing / invalid / revoked API key"},"403":{"description":"Workspace plan does not include API access"},"429":{"description":"Rate limit exceeded — see Retry-After + X-RateLimit-* headers"}}}},"/findings/status":{"post":{"summary":"Write back a finding's status/disposition","operationId":"set_finding_status","tags":["Findings"],"description":"Requires a WRITE-scoped API key. Sets a finding's operator status (incl. risk_accepted / false_positive) and/or assignee/note.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"findingId":{"type":"string"},"status":{"type":"string","enum":["open","approved","snoozed","fixed","risk_accepted","false_positive"]},"assignedTo":{"type":["string","null"]},"note":{"type":["string","null"]}},"required":["findingId"]}}}},"responses":{"200":{"description":"Updated"},"400":{"description":"Bad request / invalid status"},"403":{"description":"Read-only key or no API access"},"404":{"description":"Finding not found"}}}},"/compliance":{"get":{"summary":"Compliance evidence pack","operationId":"get_compliance","tags":["Compliance"],"description":"Coverage across the assessed CIS Microsoft 365 Foundations areas, with every mapped control's current disposition (open / remediated / risk_accepted / false_positive) and the last operator action — the machine-readable form of the console's evidence pack.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ComplianceEvidence"}}}},"401":{"description":"Missing / invalid / revoked API key"},"403":{"description":"Workspace plan does not include API access"},"404":{"description":"No completed scan yet"},"429":{"description":"Rate limit exceeded — see Retry-After + X-RateLimit-* headers"}}}},"/users":{"get":{"summary":"List identity inventory","operationId":"list_users","tags":["Inventory"],"parameters":[{"name":"include_archived","in":"query","schema":{"type":"boolean","default":false},"description":"Include soft-archived items"},{"name":"limit","in":"query","schema":{"type":"integer","default":500,"maximum":1000},"description":"Page size (max 1000)"},{"name":"offset","in":"query","schema":{"type":"integer","default":0},"description":"Items to skip"},{"name":"updated_after","in":"query","schema":{"type":"string","format":"date-time"},"description":"Only items whose last_seen is after this ISO timestamp (incremental sync)"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"workspace":{"type":"string"},"generated_at":{"type":"string","format":"date-time"},"total":{"type":"integer","description":"Total matching items (before pagination)"},"count":{"type":"integer","description":"Items in this page"},"limit":{"type":"integer"},"offset":{"type":"integer"},"has_more":{"type":"boolean"},"users":{"type":"array","items":{"$ref":"#/components/schemas/User"}}},"required":["workspace","generated_at","total","count","users"]}}}},"401":{"description":"Missing / invalid / revoked API key"},"403":{"description":"Workspace plan does not include API access"},"429":{"description":"Rate limit exceeded — see Retry-After + X-RateLimit-* headers"}}}},"/users/{id}/devices":{"get":{"summary":"List a user's devices","operationId":"list_user_devices","tags":["Inventory"],"description":"The devices a user is the Intune primary user of — the reverse of a device's `primary_user`. `id` is the canonical correlated identity id from /users. Absence of a device here is not a claim of no ownership; only Intune-managed devices carry a primary user.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"workspace":{"type":"string"},"generated_at":{"type":"string","format":"date-time"},"count":{"type":"integer"},"devices":{"type":"array","items":{"$ref":"#/components/schemas/UserDevice"}}},"required":["workspace","generated_at","count","devices"]}}}},"401":{"description":"Missing / invalid / revoked API key"},"403":{"description":"Workspace plan does not include API access"},"404":{"description":"No such user in this workspace"},"429":{"description":"Rate limit exceeded — see Retry-After + X-RateLimit-* headers"}}}},"/devices":{"get":{"summary":"List device inventory","operationId":"list_devices","tags":["Inventory"],"parameters":[{"name":"include_archived","in":"query","schema":{"type":"boolean","default":false},"description":"Include soft-archived items"},{"name":"limit","in":"query","schema":{"type":"integer","default":500,"maximum":1000},"description":"Page size (max 1000)"},{"name":"offset","in":"query","schema":{"type":"integer","default":0},"description":"Items to skip"},{"name":"updated_after","in":"query","schema":{"type":"string","format":"date-time"},"description":"Only items whose last_seen is after this ISO timestamp (incremental sync)"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"workspace":{"type":"string"},"generated_at":{"type":"string","format":"date-time"},"total":{"type":"integer","description":"Total matching items (before pagination)"},"count":{"type":"integer","description":"Items in this page"},"limit":{"type":"integer"},"offset":{"type":"integer"},"has_more":{"type":"boolean"},"devices":{"type":"array","items":{"$ref":"#/components/schemas/Asset"}},"as_of_scan":{"type":["integer","null"],"description":"Scan that last refreshed the vulnerability data"},"stale":{"type":"boolean","description":"True when the latest scan did not assess Defender vulnerabilities — the data may contain already-remediated entries"}},"required":["workspace","generated_at","total","count","devices"],"description":"as_of_scan/stale are present only when at least one returned device carries a `cves` rollup; when stale is true those rollup counts may include already-remediated CVEs."}}}},"401":{"description":"Missing / invalid / revoked API key"},"403":{"description":"Workspace plan does not include API access"},"429":{"description":"Rate limit exceeded — see Retry-After + X-RateLimit-* headers"}}}},"/apps":{"get":{"summary":"List application inventory","operationId":"list_apps","tags":["Inventory"],"parameters":[{"name":"include_archived","in":"query","schema":{"type":"boolean","default":false},"description":"Include soft-archived items"},{"name":"limit","in":"query","schema":{"type":"integer","default":500,"maximum":1000},"description":"Page size (max 1000)"},{"name":"offset","in":"query","schema":{"type":"integer","default":0},"description":"Items to skip"},{"name":"updated_after","in":"query","schema":{"type":"string","format":"date-time"},"description":"Only items whose last_seen is after this ISO timestamp (incremental sync)"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"workspace":{"type":"string"},"generated_at":{"type":"string","format":"date-time"},"total":{"type":"integer","description":"Total matching items (before pagination)"},"count":{"type":"integer","description":"Items in this page"},"limit":{"type":"integer"},"offset":{"type":"integer"},"has_more":{"type":"boolean"},"apps":{"type":"array","items":{"$ref":"#/components/schemas/Asset"}}},"required":["workspace","generated_at","total","count","apps"]}}}},"401":{"description":"Missing / invalid / revoked API key"},"403":{"description":"Workspace plan does not include API access"},"429":{"description":"Rate limit exceeded — see Retry-After + X-RateLimit-* headers"}}}},"/domains":{"get":{"summary":"List domain inventory","operationId":"list_domains","tags":["Inventory"],"parameters":[{"name":"include_archived","in":"query","schema":{"type":"boolean","default":false},"description":"Include soft-archived items"},{"name":"limit","in":"query","schema":{"type":"integer","default":500,"maximum":1000},"description":"Page size (max 1000)"},{"name":"offset","in":"query","schema":{"type":"integer","default":0},"description":"Items to skip"},{"name":"updated_after","in":"query","schema":{"type":"string","format":"date-time"},"description":"Only items whose last_seen is after this ISO timestamp (incremental sync)"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"workspace":{"type":"string"},"generated_at":{"type":"string","format":"date-time"},"total":{"type":"integer","description":"Total matching items (before pagination)"},"count":{"type":"integer","description":"Items in this page"},"limit":{"type":"integer"},"offset":{"type":"integer"},"has_more":{"type":"boolean"},"domains":{"type":"array","items":{"$ref":"#/components/schemas/Asset"}}},"required":["workspace","generated_at","total","count","domains"]}}}},"401":{"description":"Missing / invalid / revoked API key"},"403":{"description":"Workspace plan does not include API access"},"429":{"description":"Rate limit exceeded — see Retry-After + X-RateLimit-* headers"}}}},"/vulnerabilities":{"get":{"summary":"List vulnerabilities","operationId":"list_vulnerabilities","tags":["Vulnerabilities"],"description":"Your vulnerability inventory from Defender Vulnerability Management, worst first. Includes Defender-issued `TVM-…` ids alongside public CVEs (see the `type` field). Every response carries `as_of_scan` and `stale` — when `stale` is true the data was NOT refreshed by the latest scan and may contain already-remediated vulnerabilities. `updated_after` is not supported: the set is replaced wholesale per scan, so there is no per-row mutation timestamp.","parameters":[{"name":"severity","in":"query","schema":{"type":"string"},"description":"Comma-separated: Critical,High,Medium,Low"},{"name":"min_cvss","in":"query","schema":{"type":"number","minimum":0,"maximum":10},"description":"At-or-above this CVSS v3 base score"},{"name":"exploit_available","in":"query","schema":{"type":"boolean"},"description":"Only vulnerabilities with a known public exploit or exploit kit"},{"name":"affected_only","in":"query","schema":{"type":"boolean"},"description":"Exclude vulnerabilities we correlate no device for"},{"name":"type","in":"query","schema":{"type":"string","enum":["cve","tvm"]},"description":"cve = public CVEs only; tvm = Defender-issued TVM ids only"},{"name":"limit","in":"query","schema":{"type":"integer","default":500,"maximum":1000},"description":"Page size (max 1000)"},{"name":"offset","in":"query","schema":{"type":"integer","default":0},"description":"Items to skip"},{"name":"as_of_scan","in":"query","schema":{"type":"integer"},"description":"Echo the as_of_scan from your first page. If a scan has replaced the data since, the request fails 409 resync_required rather than serving a torn read."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"workspace":{"type":"string"},"generated_at":{"type":"string","format":"date-time"},"total":{"type":"integer","description":"Total matching items (before pagination)"},"count":{"type":"integer","description":"Items in this page"},"limit":{"type":"integer"},"offset":{"type":"integer"},"has_more":{"type":"boolean"},"vulnerabilities":{"type":"array","items":{"$ref":"#/components/schemas/Vulnerability"}},"as_of_scan":{"type":["integer","null"],"description":"Scan that last refreshed the vulnerability data"},"stale":{"type":"boolean","description":"True when the latest scan did not assess Defender vulnerabilities — the data may contain already-remediated entries"}},"required":["workspace","generated_at","total","count","vulnerabilities"]}}}},"401":{"description":"Missing / invalid / revoked API key"},"403":{"description":"Workspace plan does not include API access"},"409":{"description":"resync_required — a scan replaced the vulnerability data mid-pagination; restart the walk"},"429":{"description":"Rate limit exceeded — see Retry-After + X-RateLimit-* headers"}}}},"/vulnerabilities/{id}":{"get":{"summary":"Get a vulnerability and every device it affects","operationId":"get_vulnerability","tags":["Vulnerabilities"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"CVE-2024-21412 or TVM-2026-0001"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VulnerabilityDetail"}}}},"401":{"description":"Missing / invalid / revoked API key"},"403":{"description":"Workspace plan does not include API access"},"404":{"description":"No such vulnerability in this workspace"},"429":{"description":"Rate limit exceeded — see Retry-After + X-RateLimit-* headers"}}}},"/cves":{"get":{"summary":"List vulnerabilities (DEPRECATED alias of /vulnerabilities)","operationId":"list_cves","tags":["Vulnerabilities"],"deprecated":true,"description":"DEPRECATED — identical to /vulnerabilities; kept because /cves is already public. Prefer /vulnerabilities. Your vulnerability inventory from Defender Vulnerability Management, worst first. Includes Defender-issued `TVM-…` ids alongside public CVEs (see the `type` field). Every response carries `as_of_scan` and `stale` — when `stale` is true the data was NOT refreshed by the latest scan and may contain already-remediated vulnerabilities. `updated_after` is not supported: the set is replaced wholesale per scan, so there is no per-row mutation timestamp.","parameters":[{"name":"severity","in":"query","schema":{"type":"string"},"description":"Comma-separated: Critical,High,Medium,Low"},{"name":"min_cvss","in":"query","schema":{"type":"number","minimum":0,"maximum":10},"description":"At-or-above this CVSS v3 base score"},{"name":"exploit_available","in":"query","schema":{"type":"boolean"},"description":"Only vulnerabilities with a known public exploit or exploit kit"},{"name":"affected_only","in":"query","schema":{"type":"boolean"},"description":"Exclude vulnerabilities we correlate no device for"},{"name":"type","in":"query","schema":{"type":"string","enum":["cve","tvm"]},"description":"cve = public CVEs only; tvm = Defender-issued TVM ids only"},{"name":"limit","in":"query","schema":{"type":"integer","default":500,"maximum":1000},"description":"Page size (max 1000)"},{"name":"offset","in":"query","schema":{"type":"integer","default":0},"description":"Items to skip"},{"name":"as_of_scan","in":"query","schema":{"type":"integer"},"description":"Echo the as_of_scan from your first page. If a scan has replaced the data since, the request fails 409 resync_required rather than serving a torn read."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"workspace":{"type":"string"},"generated_at":{"type":"string","format":"date-time"},"total":{"type":"integer","description":"Total matching items (before pagination)"},"count":{"type":"integer","description":"Items in this page"},"limit":{"type":"integer"},"offset":{"type":"integer"},"has_more":{"type":"boolean"},"cves":{"type":"array","items":{"$ref":"#/components/schemas/Vulnerability"}},"as_of_scan":{"type":["integer","null"],"description":"Scan that last refreshed the vulnerability data"},"stale":{"type":"boolean","description":"True when the latest scan did not assess Defender vulnerabilities — the data may contain already-remediated entries"}},"required":["workspace","generated_at","total","count","cves"]}}}},"401":{"description":"Missing / invalid / revoked API key"},"403":{"description":"Workspace plan does not include API access"},"409":{"description":"resync_required — a scan replaced the vulnerability data mid-pagination; restart the walk"},"429":{"description":"Rate limit exceeded — see Retry-After + X-RateLimit-* headers"}}}},"/cves/{id}":{"get":{"summary":"Get a vulnerability and its devices (DEPRECATED alias)","operationId":"get_cve","tags":["Vulnerabilities"],"deprecated":true,"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"CVE-2024-21412 or TVM-2026-0001"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VulnerabilityDetail"}}}},"401":{"description":"Missing / invalid / revoked API key"},"403":{"description":"Workspace plan does not include API access"},"404":{"description":"No such vulnerability in this workspace"},"429":{"description":"Rate limit exceeded — see Retry-After + X-RateLimit-* headers"}}}},"/devices/{id}/cves":{"get":{"summary":"List a device's vulnerabilities","operationId":"list_device_cves","tags":["Vulnerabilities"],"description":"Every vulnerability on one device, with the vulnerable software carrying it. `id` is the canonical correlated asset id from /devices, not a Defender machine id.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"limit","in":"query","schema":{"type":"integer","default":500,"maximum":1000},"description":"Page size (max 1000)"},{"name":"offset","in":"query","schema":{"type":"integer","default":0},"description":"Items to skip"},{"name":"as_of_scan","in":"query","schema":{"type":"integer"},"description":"Echo the as_of_scan from your first page. If a scan has replaced the data since, the request fails 409 resync_required rather than serving a torn read."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"workspace":{"type":"string"},"generated_at":{"type":"string","format":"date-time"},"total":{"type":"integer","description":"Total matching items (before pagination)"},"count":{"type":"integer","description":"Items in this page"},"limit":{"type":"integer"},"offset":{"type":"integer"},"has_more":{"type":"boolean"},"cves":{"type":"array","items":{"$ref":"#/components/schemas/DeviceCve"}},"as_of_scan":{"type":["integer","null"],"description":"Scan that last refreshed the vulnerability data"},"stale":{"type":"boolean","description":"True when the latest scan did not assess Defender vulnerabilities — the data may contain already-remediated entries"},"device":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":["string","null"]}}}},"required":["workspace","generated_at","total","count","cves"]}}}},"401":{"description":"Missing / invalid / revoked API key"},"403":{"description":"Workspace plan does not include API access"},"404":{"description":"No such device in this workspace"},"409":{"description":"resync_required — a scan replaced the vulnerability data mid-pagination; restart the walk"},"429":{"description":"Rate limit exceeded — see Retry-After + X-RateLimit-* headers"}}}},"/device-vulnerabilities":{"get":{"summary":"The flat device↔vulnerability edge feed","operationId":"list_device_vulnerabilities","tags":["Vulnerabilities"],"description":"The fleet-wide machine feed: one row per (device, vulnerability, software) edge across your whole tenant, worst first. This is the bulk-ingest surface for a SIEM/CMDB — KEYSET paginated (cursor, not offset) so it never skips or duplicates a row under an in-flight scan. Carries denormalised device + CVE scalars but NOT the CVE `description` (fetch that once from /vulnerabilities/{id}). `updated_after` is intentionally unavailable — `last_seen` is restamped every scan, so a timestamp filter would return everything. For REMOVALS (a patched edge is a hard delete), use /vuln-changes.","parameters":[{"name":"device_id","in":"query","schema":{"type":"string","format":"uuid"},"description":"Only edges on this device"},{"name":"cve_id","in":"query","schema":{"type":"string"},"description":"Only edges for this vulnerability id"},{"name":"severity","in":"query","schema":{"type":"string"},"description":"Comma-separated: Critical,High,Medium,Low"},{"name":"min_cvss","in":"query","schema":{"type":"number","minimum":0,"maximum":10},"description":"At-or-above this CVSS v3 base score"},{"name":"exploit_available","in":"query","schema":{"type":"boolean"},"description":"Only edges whose CVE has a known public exploit"},{"name":"type","in":"query","schema":{"type":"string","enum":["cve","tvm"]},"description":"cve = public CVEs only; tvm = Defender-issued TVM ids only"},{"name":"new_since","in":"query","schema":{"type":"integer"},"description":"Only edges ADDED since this scan id (additions only; removals via /vuln-changes)"},{"name":"cursor","in":"query","schema":{"type":"string"},"description":"Opaque keyset cursor from the previous page's next_cursor. HMAC-signed and snapshot-bound: a tampered/garbage cursor is rejected 400; a cursor minted against a superseded scan is rejected 409 resync_required — restart from the first page."},{"name":"limit","in":"query","schema":{"type":"integer","default":500,"maximum":1000},"description":"Page size (max 1000)"},{"name":"as_of_scan","in":"query","schema":{"type":"integer"},"description":"On the FIRST page (no cursor), echo the as_of_scan you started with → 409 if a scan has since replaced the data. Subsequent pages carry the snapshot inside the cursor."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"workspace":{"type":"string"},"generated_at":{"type":"string","format":"date-time"},"as_of_scan":{"type":["integer","null"],"description":"Scan that last refreshed the vulnerability data"},"stale":{"type":"boolean","description":"True when the latest scan did not assess Defender vulnerabilities — the data may contain already-remediated entries"},"count":{"type":"integer","description":"Items in this page"},"next_cursor":{"type":["string","null"],"description":"Pass as ?cursor to fetch the next page; null when the walk is exhausted"},"has_more":{"type":"boolean"},"device_vulnerabilities":{"type":"array","items":{"$ref":"#/components/schemas/DeviceVulnerability"}}},"required":["workspace","generated_at","count","device_vulnerabilities"]}}}},"400":{"description":"Tampered/garbage cursor, or an out-of-range severity/min_cvss"},"401":{"description":"Missing / invalid / revoked API key"},"403":{"description":"Workspace plan does not include API access"},"409":{"description":"resync_required — a scan replaced the vulnerability data mid-pagination; restart the walk"},"429":{"description":"Rate limit exceeded — see Retry-After + X-RateLimit-* headers"}}}},"/vuln-changes":{"get":{"summary":"The device↔vulnerability delta (added / removed) feed","operationId":"list_vuln_changes","tags":["Vulnerabilities"],"description":"Which device↔vulnerability edges were ADDED or REMOVED since a given scan — the ONLY honest source of removals (a remediated edge is a hard delete with no tombstone in the edge feed). A SIEM uses this to CLOSE the ticket it opened, not only to open one. The delta unit is `scan_id`, never a timestamp (last_seen is restamped every scan). Keyset paginated on the monotonic event id. When `since_scan` predates the retained event history, `resync_required: true` is returned with empty changes — do a full /device-vulnerabilities pull instead of trusting a lossy delta.","parameters":[{"name":"since_scan","in":"query","required":true,"schema":{"type":"integer"},"description":"The as_of_scan of your last sync — return every edge change recorded after it."},{"name":"cursor","in":"query","schema":{"type":"string"},"description":"Opaque keyset cursor from the previous page's next_cursor. HMAC-signed and snapshot-bound: a tampered/garbage cursor is rejected 400; a cursor minted against a superseded scan is rejected 409 resync_required — restart from the first page."},{"name":"limit","in":"query","schema":{"type":"integer","default":500,"maximum":1000},"description":"Page size (max 1000)"}],"responses":{"200":{"description":"OK — includes resync_required:true (with empty changes) when since_scan is older than retained history","content":{"application/json":{"schema":{"type":"object","properties":{"workspace":{"type":"string"},"generated_at":{"type":"string","format":"date-time"},"since_scan":{"type":"integer"},"as_of_scan":{"type":["integer","null"]},"resync_required":{"type":"boolean","description":"since_scan predates retained event history — full /device-vulnerabilities resync required"},"count":{"type":"integer","description":"Items in this page"},"next_cursor":{"type":["string","null"],"description":"Pass as ?cursor to fetch the next page; null when the walk is exhausted"},"has_more":{"type":"boolean"},"changes":{"type":"array","items":{"$ref":"#/components/schemas/VulnChange"}}},"required":["workspace","generated_at","count","changes"]}}}},"400":{"description":"since_scan missing/invalid, or a tampered/mismatched cursor"},"401":{"description":"Missing / invalid / revoked API key"},"403":{"description":"Workspace plan does not include API access"},"429":{"description":"Rate limit exceeded — see Retry-After + X-RateLimit-* headers"}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"bstn_…","description":"API key from console → Settings"}},"schemas":{"Vulnerability":{"type":"object","properties":{"id":{"type":"string","description":"CVE-2024-21412, or a Defender-issued TVM-2026-0001"},"type":{"type":"string","enum":["cve","tvm"],"description":"cve = public CVE; tvm = Defender-issued id with no CVE assigned"},"severity":{"type":["string","null"],"enum":["Critical","High","Medium","Low",null]},"cvss":{"type":["number","null"],"description":"CVSS v3 base score"},"published_at":{"type":["string","null"],"format":"date-time"},"description":{"type":["string","null"]},"exploit_available":{"type":"boolean","description":"Defender reports a public exploit or exploit-kit availability"},"affected_devices":{"type":"integer","description":"Devices correlated in YOUR inventory"},"reported_exposed_devices":{"type":["integer","null"],"description":"Defender's own exposed-machine count; may exceed affected_devices (stale/uncorrelated records)"},"reference_url":{"type":"string","description":"NVD for CVEs, the Defender portal for TVM ids"}}},"DeviceCve":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","enum":["cve","tvm"]},"severity":{"type":["string","null"]},"cvss":{"type":["number","null"]},"published_at":{"type":["string","null"],"format":"date-time"},"description":{"type":["string","null"]},"exploit_available":{"type":"boolean"},"software_name":{"type":["string","null"],"description":"The vulnerable package on this device"},"software_version":{"type":["string","null"]},"reference_url":{"type":"string"}}},"DeviceVulnerability":{"type":"object","description":"One (device, vulnerability, software) edge — the flat feed row. Denormalises the device and CVE scalars you need to route/prioritise without a second call; omits the multi-KB CVE description.","properties":{"device_id":{"type":"string","format":"uuid","description":"Canonical correlated asset id (join to /devices/{id})"},"device_name":{"type":["string","null"]},"hostname":{"type":["string","null"]},"cve_id":{"type":"string"},"type":{"type":"string","enum":["cve","tvm"]},"severity":{"type":["string","null"]},"cvss":{"type":["number","null"]},"exploit_available":{"type":"boolean"},"software_name":{"type":["string","null"]},"software_version":{"type":["string","null"]},"first_seen":{"type":"string","format":"date-time","description":"When this edge first appeared"},"last_seen":{"type":"string","format":"date-time","description":"Restamped every scan the edge is still present — not a change signal; use /vuln-changes"},"reference_url":{"type":"string"}}},"VulnChange":{"type":"object","description":"An append-only device↔vulnerability change event.","properties":{"change":{"type":"string","enum":["added","removed"]},"scan_id":{"type":"integer","description":"The scan that recorded the change"},"device_id":{"type":"string","format":"uuid"},"cve_id":{"type":"string"},"type":{"type":"string","enum":["cve","tvm"]},"software_name":{"type":["string","null"]},"ts":{"type":"string","format":"date-time"}}},"VulnerabilityDetail":{"type":"object","properties":{"workspace":{"type":"string"},"generated_at":{"type":"string","format":"date-time"},"as_of_scan":{"type":["integer","null"],"description":"Scan that last refreshed the vulnerability data"},"stale":{"type":"boolean","description":"True when the latest scan did not assess Defender vulnerabilities — the data may contain already-remediated entries"},"vulnerability":{"$ref":"#/components/schemas/Vulnerability"},"cve":{"$ref":"#/components/schemas/Vulnerability","description":"Back-compat alias of `vulnerability` (the /cves path historically returned `cve`)"},"impacted_devices":{"type":"array","description":"One entry per device — length always equals the vulnerability's affected_devices. A device carrying it via several packages lists them all in `software`.","items":{"type":"object","properties":{"device_id":{"type":"string","format":"uuid"},"name":{"type":["string","null"]},"software":{"type":"array","items":{"type":"object","properties":{"name":{"type":["string","null"]},"version":{"type":["string","null"]}}}},"last_seen":{"type":"string","format":"date-time"}}}}}},"Finding":{"type":"object","properties":{"id":{"type":"string"},"control":{"type":"string"},"severity":{"type":"string","enum":["Critical","High","Medium","Low"]},"priority":{"type":["integer","null"]},"status":{"type":"string"},"category":{"type":["string","null"]},"title":{"type":"string"},"affected_count":{"type":"integer"},"first_seen":{"type":"string","format":"date-time"},"last_seen":{"type":"string","format":"date-time"},"exploitability":{"type":["integer","null"]},"action_class":{"type":["string","null"]},"remediation":{"type":["string","null"]},"remediation_source":{"type":["string","null"],"enum":["canonical","catalog","ai",null]}}},"User":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Our canonical (correlated) asset id"},"entra_object_id":{"type":["string","null"],"format":"uuid","description":"Entra directory object id — use this against Microsoft Graph"},"name":{"type":["string","null"]},"upn":{"type":["string","null"]},"user_type":{"type":["string","null"]},"enabled":{"type":["boolean","null"]},"last_sign_in":{"type":["string","null"]},"global_admin":{"type":"boolean"},"mfa_gap":{"type":"boolean"},"risky":{"type":"boolean"},"finding_count":{"type":"integer"}}},"UserDevice":{"type":"object","description":"A device under a user (the reverse of a device's primary_user).","properties":{"device_id":{"type":"string","format":"uuid","description":"Canonical correlated asset id (join to /devices/{id})"},"name":{"type":["string","null"]},"last_seen":{"type":"string","format":"date-time"}}},"Asset":{"type":"object","description":"An inventory asset — device, application, or domain (type field distinguishes). For domains, finding_count reflects SPF/DMARC/DKIM posture gaps.","properties":{"id":{"type":"string","format":"uuid","description":"Our canonical (correlated) asset id"},"name":{"type":["string","null"]},"type":{"type":"string"},"os":{"type":["string","null"]},"device_type":{"type":["string","null"]},"identifiers":{"type":"object","description":"Source-system ids for this asset — only the keys we actually hold are present.","properties":{"entra_device_id":{"type":"string","format":"uuid","description":"Entra deviceId"},"entra_object_id":{"type":"string","format":"uuid","description":"Entra directory object id"},"intune_id":{"type":"string","format":"uuid","description":"Intune managedDeviceId"},"defender_id":{"type":"string","description":"Defender for Endpoint machine id"},"serial_number":{"type":"string"},"hostname":{"type":"string"},"app_id":{"type":"string","description":"Entra application (client) id — application assets"}}},"sources":{"type":"array","items":{"type":"string"},"description":"Connector keys that cover this asset"},"cves":{"type":"object","description":"Vulnerability rollup (counts only — the full list is at `url`; it is never embedded, because one device can hold 500+ CVEs and a truncated array read as complete is a false negative). PRESENT only when this device has Defender for Endpoint coverage AND the workspace holds vulnerability data. Its ABSENCE means the device has no vulnerability visibility; `total: 0` means Defender covers it and reported none. Those are different claims — do not treat absence as clean.","properties":{"total":{"type":"integer","description":"Distinct CVEs (a CVE carried by two packages counts once)"},"critical":{"type":"integer"},"high":{"type":"integer"},"medium":{"type":"integer"},"low":{"type":"integer"},"exploitable":{"type":"integer","description":"CVEs with a known public exploit or exploit kit"},"max_cvss":{"type":["number","null"]},"url":{"type":"string","description":"The device's full, paginated CVE list"}}},"primary_user":{"type":"object","description":"The Intune primary user of this device, resolved to a canonical identity. PRESENT only for Intune-managed devices with a primary user set. Its ABSENCE means the owner is unknown — not that the device is unowned. `id` round-trips to /api/v1/users/{id}.","properties":{"id":{"type":"string","format":"uuid","description":"Canonical correlated identity id (join to /users/{id})"},"name":{"type":["string","null"]},"upn":{"type":["string","null"]}}},"first_seen":{"type":"string","format":"date-time"},"last_seen":{"type":"string","format":"date-time"},"finding_count":{"type":"integer"},"risk_score":{"type":["integer","null"]}}},"ComplianceEvidence":{"type":"object","properties":{"workspace":{"type":"string"},"generated_at":{"type":"string","format":"date-time"},"framework":{"type":"string"},"scan":{"type":["object","null"],"properties":{"id":{"type":"integer"},"finishedAt":{"type":["string","null"],"format":"date-time"}}},"summary":{"type":"object","properties":{"areas_assessed":{"type":"integer"},"areas_total":{"type":"integer"},"dispositions":{"type":"object","additionalProperties":{"type":"integer"}}}},"areas":{"type":"array","items":{"type":"object","properties":{"area":{"type":"string"},"cis_ref":{"type":"string"},"status":{"type":"string","enum":["action","review","pass","unassessed"]},"assessed":{"type":"boolean"},"partial":{"type":"boolean"},"severity_counts":{"type":"object","properties":{"critical":{"type":"integer"},"high":{"type":"integer"},"medium":{"type":"integer"},"low":{"type":"integer"}}},"controls":{"type":"array","items":{"type":"object","properties":{"control":{"type":"string"},"title":{"type":"string"},"severity":{"type":"string"},"disposition":{"type":"string","description":"open | approved | snoozed | fixed | resolved | risk_accepted | false_positive"},"affected_count":{"type":"integer"},"first_seen":{"type":"string","format":"date-time"},"last_seen":{"type":"string","format":"date-time"},"assigned_to":{"type":["string","null"]},"note":{"type":["string","null"]},"remediation_source":{"type":["string","null"]},"last_action":{"type":["object","null"],"properties":{"actor":{"type":["string","null"]},"action":{"type":"string"},"detail":{"type":["string","null"]},"ts":{"type":"string"}}}}}}}}},"not_assessed":{"type":"array","items":{"type":"object","properties":{"area":{"type":"string"},"reason":{"type":["string","null"]}}}},"frameworks":{"type":"array","description":"The assessed posture crosswalked onto SOC 2 / ISO 27001 / NIST CSF","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"ref":{"type":"string"},"controls_assessed":{"type":"integer"},"controls_total":{"type":"integer"},"controls":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string"},"status":{"type":"string","enum":["action","review","pass","unassessed"]},"assessed":{"type":"boolean"},"partial":{"type":"boolean","description":"assessed but only part of the control's mapped scope was scanned"},"mapped_areas":{"type":"array","items":{"type":"string"},"description":"the full crosswalk"},"assessed_via":{"type":"array","items":{"type":"string"},"description":"only the mapped areas actually scanned (honest evidence sources)"}}}}}}}}}}}}