REST API
VulnScout exposes a REST API served by the Flask backend, available at http://localhost:7275/api/ by default.
All endpoints return JSON unless otherwise noted. Routes are registered directly on the Flask application without URL prefixes or blueprints.
Version & Status
Get Server Version
GET /api/version
Returns the running VulnScout version.
Response:
{ "version": "1.2.3" }
The value comes from the VULNSCOUT_VERSION environment variable ("unknown" when unset).
Get Initial Scan Status
GET /api/scan/status
Returns the progress of the container entrypoint import script. This endpoint is available even while the import is still running (all other /api/* routes return 503 until the script finishes).
Response:
{
"status": "running",
"maxsteps": 8,
"step": 3,
"message": "Merging SBOMs"
}
Field |
Type |
Description |
|---|---|---|
|
string |
|
|
int |
Total number of import steps |
|
int |
Current step number |
|
string |
Human-readable progress label |
Config
Get Current Config
GET /api/config
Returns the active project and variant based on the PROJECT_NAME / VARIANT_NAME environment variables, or the first project in the database.
Response:
{
"project": { "id": "...", "name": "..." },
"variant": { "id": "...", "name": "...", "project_id": "..." }
}
Fields may be null if no project or variant exists.
Projects
List Projects
GET /api/projects
Returns all projects.
Response: JSON array of project objects.
Create Project
POST /api/projects
Request body:
{ "name": "my-project" }
Response: 201 Created — serialized project. 409 Conflict if the name is already taken.
Rename Project
PATCH /api/projects/<project_id>/rename
Request body:
{ "name": "new-name" }
Response: Serialized project. 409 Conflict if the name is already taken.
Delete Project
DELETE /api/projects/<project_id>
Deletes the project and all its associated data (variants, scans, findings).
Response:
{ "message": "Project deleted." }
Variants
List All Variants
GET /api/variants
Returns all variants across all projects.
List Variants for a Project
GET /api/projects/<project_id>/variants
Response: JSON array of variant objects. 404 if project not found.
Create Variant
POST /api/projects/<project_id>/variants
Request body:
{ "name": "x86_64" }
Response: 201 Created. 409 Conflict if the name already exists within the project.
Rename Variant
PATCH /api/variants/<variant_id>/rename
Request body:
{ "name": "aarch64" }
Response: Serialized variant. 409 Conflict if the name is already taken within the project.
Delete Variant
DELETE /api/variants/<variant_id>
Response:
{ "message": "Variant deleted." }
Packages
List Packages
GET /api/packages
Query parameters:
Parameter |
Type |
Description |
|---|---|---|
|
UUID |
Filter by variant |
|
UUID |
Filter by project |
|
UUID |
Second variant for comparison (requires |
|
string |
|
|
string |
|
Response: JSON array of package objects enriched with variants and sources fields.
When compare_variant_id is provided together with variant_id, the endpoint computes a diff or intersection of packages between the two variants.
Vulnerabilities
List Vulnerabilities
GET /api/vulnerabilities
Query parameters:
Parameter |
Type |
Description |
|---|---|---|
|
UUID |
Filter by variant |
|
UUID |
Filter by project |
|
UUID |
Second variant for comparison |
|
string |
|
|
string |
|
Response: JSON array of vulnerability objects enriched with packages_current, variants, first_scan_date and found_by.
Note: When
variant_idorproject_idis provided, findings from tool scans (Grype, NVD, OSV) are automatically filtered to only include packages present in the variant’s active SBOM. This prevents stale or cross-variant vulnerabilities from appearing. SBOM-scan findings are always included unconditionally.
Get Single Vulnerability
GET /api/vulnerabilities/<id>
Response: Vulnerability object. 404 if not found.
Update Vulnerability
PATCH /api/vulnerabilities/<id>
Update effort estimates and/or CVSS scoring for a vulnerability.
Request body:
{
"effort": {
"optimistic": 3600,
"likely": 7200,
"pessimistic": 14400
},
"cvss": {
"base_score": 7.5,
"vector_string": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
"version": "3.1"
},
"variant_id": "optional-uuid"
}
Effort values can be integers (seconds) or ISO 8601 duration strings.
Response: Updated vulnerability object.
Batch Update Vulnerabilities
PATCH /api/vulnerabilities/batch
Request body:
{
"vulnerabilities": [
{ "id": "CVE-2024-1234", "effort": { "likely": 3600 } },
{ "id": "CVE-2024-5678", "cvss": { "base_score": 9.0 } }
]
}
Response:
{
"status": "success",
"vulnerabilities": [...],
"count": 2,
"errors": [],
"error_count": 0
}
Assessments
List Assessments
GET /api/assessments
Query parameters:
Parameter |
Type |
Description |
|---|---|---|
|
UUID |
Filter by variant |
|
UUID |
Filter by project |
|
string |
|
Get Single Assessment
GET /api/assessments/<assessment_id>
Response: Assessment object. 404 if not found.
List Assessments for a Vulnerability
GET /api/vulnerabilities/<vuln_id>/assessments
Query parameters: format ("list" or "dict").
List Variants for a Vulnerability
GET /api/vulnerabilities/<vuln_id>/variants
Returns all distinct variants that have a finding for this vulnerability.
Response:
[
{ "id": "...", "name": "x86_64", "project_id": "..." }
]
Create Assessment
POST /api/vulnerabilities/<vuln_id>/assessments
Request body:
{
"packages": ["package-name@1.0.0"],
"status": "affected",
"variant_id": "optional-uuid",
"status_notes": "Confirmed by manual review",
"justification": "",
"impact_statement": "",
"workaround": "",
"responses": ["update"]
}
Response:
{
"status": "success",
"assessments": [...],
"assessment": { ... }
}
Batch Create Assessments
POST /api/assessments/batch
Request body:
{
"assessments": [
{ "vuln_id": "CVE-2024-1234", "packages": ["pkg@1.0"], "status": "affected" },
{ "vuln_id": "CVE-2024-5678", "packages": ["pkg@2.0"], "status": "not_affected", "justification": "code_not_reachable" }
]
}
Response:
{
"status": "success",
"assessments": [...],
"count": 2,
"errors": [],
"error_count": 0
}
Update Assessment
PUT /api/assessments/<assessment_id>
PATCH /api/assessments/<assessment_id>
Request body:
{
"status": "not_affected",
"status_notes": "False positive",
"justification": "vulnerable_code_not_present",
"impact_statement": "Component is not included in our build",
"workaround": ""
}
All fields are optional.
Response:
{
"status": "success",
"assessment": { ... }
}
Delete Assessment
DELETE /api/assessments/<assessment_id>
Response:
{
"status": "success",
"message": "Assessment deleted successfully"
}
Review Assessments (User-Created)
List Review Assessments
GET /api/assessments/review
Returns only user-created assessments (not scan-imported).
Query parameters: variant_id, project_id.
Export Review Assessments
GET /api/assessments/review/export
Downloads a .tar.gz archive containing one OpenVEX JSON file per variant.
Query parameters:
Parameter |
Type |
Description |
|---|---|---|
|
string |
Author name in the export (default: |
Response: Binary .tar.gz file (Content-Type: application/gzip).
Import Review Assessments
POST /api/assessments/review/import
Upload an OpenVEX .json or .tar.gz file. For .tar.gz archives, filenames inside must match variant names.
Request: Multipart form-data with a file field.
Response:
{
"status": "success",
"imported": 15,
"skipped": 2,
"errors": []
}
Custom Data Import / Export
Export Custom Data
GET /api/assessments/review/export-custom-data
Exports all handmade (review) assessments, custom CVSS scores, and time estimates as a single JSON file.
Query parameters:
Parameter |
Type |
Description |
|---|---|---|
|
UUID |
Restrict to a single variant |
|
UUID |
Restrict to all variants of a project |
Response: JSON file download (Content-Disposition: attachment; filename=custom_data.json) containing:
{
"version": "1.0",
"assessments": [...],
"cvss": [...],
"time_estimates": [...]
}
Returns 404 if there is no custom data to export.
Import Custom Data
POST /api/assessments/review/import-custom-data
Imports assessments, custom CVSS scores, and time estimates from a custom-data JSON file.
Accepts either:
multipart/form-datawith afilefield containing a.jsonfile.application/jsonbody with the custom-data payload directly.
Query parameters:
Parameter |
Type |
Description |
|---|---|---|
|
UUID |
(optional) Force all imported records to a specific variant |
Response:
{
"status": "success",
"assessments_imported": 12,
"cvss_imported": 5,
"time_estimates_imported": 3
}
Returns 400 for invalid format or unsupported content type.
Documents
List Available Documents
GET /api/documents
Returns all available report templates and SBOM export formats.
Response:
[
{ "id": "summary.adoc", "extension": "adoc", "is_template": true, "category": "report" },
{ "id": "CycloneDX 1.6", "extension": "json", "is_template": false, "category": "sbom" }
]
Generate / Download Document
GET /api/documents/<doc_name>
Generate a report from a template or export an SBOM in the requested format.
Path parameter: doc_name — template name (e.g. summary.adoc) or SBOM format (e.g. CycloneDX 1.6, SPDX 2.3, SPDX 3.0, OpenVex).
Query parameters:
Parameter |
Type |
Description |
|---|---|---|
|
string |
Target output format / extension |
|
string |
Author name for the document |
|
string |
Client name for the document |
|
string |
Export date override |
|
ISO datetime |
Ignore vulnerabilities assessed before this date |
|
float |
Only include vulns with EPSS score above this value |
Response: File download with appropriate Content-Type and Content-Disposition headers. Supports JSON, XML, PDF, and HTML outputs depending on the template.
Scans
List Scans
GET /api/scans
Returns all scans with diff statistics compared to the previous scan of the same variant.
Response: JSON array of scan objects including:
finding_count,package_count,vuln_countfindings_added,findings_removed,findings_upgradedpackages_added,packages_removed,packages_upgradedvulns_added,vulns_removedvariant_name,project_name,is_first
List Scans for a Project
GET /api/projects/<project_id>/scans
List Scans for a Variant
GET /api/variants/<variant_id>/scans
Update Scan Description
PATCH /api/scans/<scan_id>
Request body:
{ "description": "Weekly CI scan" }
Response: Updated scan object.
Delete Scan
DELETE /api/scans/<scan_id>
Deletes the scan and its observations. Findings that are no longer referenced by any observation are also removed.
Response:
{
"deleted": true,
"scan_id": "...",
"orphaned_findings_removed": 3
}
Get Scan Diff
GET /api/scans/<scan_id>/diff
Returns a detailed diff between this scan and the previous scan of the same type (and source) for the same variant.
For SBOM scans, the diff is computed against the scan result — that is, the merged union of the SBOM and active tool-scan findings filtered by SBOM packages. This means the numbers are consistent with the Scan Result badges shown in the list view.
For tool scans, the diff reflects the change in the global result (SBOM ∪ tool scans) caused by this scan. Additionally, newly_detected_* and all_* fields provide the raw tool-scan contents.
Response:
{
"scan_id": "...",
"scan_type": "sbom",
"previous_scan_id": "...",
"is_first": false,
"finding_count": 120,
"package_count": 45,
"vuln_count": 80,
"findings_added": [...],
"findings_removed": [...],
"findings_upgraded": [...],
"findings_unchanged": [...],
"packages_added": [...],
"packages_removed": [...],
"packages_upgraded": [...],
"packages_unchanged": [...],
"vulns_added": [...],
"vulns_removed": [...],
"vulns_unchanged": [...],
"newly_detected_findings": null,
"newly_detected_vulns": null,
"newly_detected_findings_list": null,
"newly_detected_vulns_list": null,
"all_findings": null,
"all_vulns": null
}
Field |
Type |
Scope |
Description |
|---|---|---|---|
|
string |
all |
|
|
array |
all |
Findings present in both the current and previous scan result |
|
array |
SBOM |
Packages present in both scans |
|
array |
all |
Vulnerability IDs present in both scan results |
|
int|null |
tool |
Count of findings new in the global result |
|
int|null |
tool |
Count of vulnerabilities new in the global result |
|
array|null |
tool |
Finding objects newly detected in the global result |
|
array|null |
tool |
Vulnerability IDs newly detected |
|
array|null |
tool |
All raw findings from this tool scan |
|
array|null |
tool |
All vulnerability IDs from this tool scan |
Get Scan Global Result
GET /api/scans/<scan_id>/global-result
Returns every active finding, vulnerability, and package at the time of the given scan, combining SBOM and tool-scan data with source attribution. Tool-scan findings are filtered to packages present in the SBOM.
Response: JSON object with findings, vulnerabilities, and packages arrays, each entry enriched with an origin field indicating the source (SBOM document name/format or tool scan source).
Export Scan Diff
GET /api/scans/<scan_id>/export-diff
Exports a single scan’s diff as a cleaned JSON download. The response strips internal fields and normalises supplier names for a portable, human-readable report.
For SBOM scans the export includes vulnerabilities, findings, packages, assessments, and (when the scan is not the first) a diff object with *_added, *_removed, *_upgraded, and *_unchanged arrays.
For tool scans the export includes vulnerabilities, findings, newly_detected_vulns, newly_detected_findings, and newly_detected_assessments.
Response: JSON file download (Content-Disposition: attachment).
Export Scan Global Result
GET /api/scans/<scan_id>/export-result
Exports a single scan’s global result (SBOM + tool data merged) as a cleaned JSON download. The response includes packages, findings, and vulnerabilities arrays with source attribution.
Response: JSON file download (Content-Disposition: attachment).
Export All Scans
GET /api/scans/export
Exports all visible scans, optionally filtered by variant or project.
Query parameters:
Parameter |
Type |
Description |
|---|---|---|
|
UUID |
Restrict to a single variant |
|
UUID |
Restrict to all variants of a project |
|
string |
|
Response: JSON array of export objects grouped per variant, served as a file download (Content-Disposition: attachment). Returns 404 if no scans match the filter.
Scan Triggers
These endpoints trigger asynchronous vulnerability scans for a specific variant. Each returns 202 Accepted immediately; use the corresponding /status endpoint to poll progress.
All trigger endpoints return 409 Conflict if a scan of the same type is already running for the variant, 404 if the variant is not found, and 503 if the required tool is unavailable (Grype only).
Trigger Grype Scan
POST /api/variants/<variant_id>/grype-scan
Runs Grype on the export, filters the results to only the variant’s SBOM packages, and merges findings back as a tool scan.
Response: 202 Accepted
{ "status": "started", "variant_id": "..." }
Progress steps: 1/4 Exporting CycloneDX → 2/4 Running Grype → 3/4 Merging results → 4/4 Processing.
Check Grype Scan Status
GET /api/variants/<variant_id>/grype-scan/status
Response:
{
"status": "running",
"error": null,
"progress": "2/4 Running Grype",
"logs": ["[1/4] CycloneDX export complete", "..."],
"total": 4,
"done_count": 1
}
Status values: "idle" (no scan started), "running", "done", "error".
Trigger NVD Scan
POST /api/variants/<variant_id>/nvd-scan
For every active package with CPE identifiers, queries the NVD CVE API and creates findings for any matched CVEs. Respects the NVD_API_KEY environment variable for higher rate limits.
Response: 202 Accepted
{ "status": "started", "variant_id": "..." }
Check NVD Scan Status
GET /api/variants/<variant_id>/nvd-scan/status
Same response shape as Grype status. total is the number of unique CPEs to query, done_count tracks progress.
Trigger OSV Scan
POST /api/variants/<variant_id>/osv-scan
For every active package with PURL identifiers, queries the OSV API. All PURLs per package are queried (e.g. generic + ecosystem-specific) so no vulnerabilities are missed.
Response: 202 Accepted
{ "status": "started", "variant_id": "..." }
Check OSV Scan Status
GET /api/variants/<variant_id>/osv-scan/status
Same response shape as Grype status. total is the number of unique PURLs to query.
SBOM Upload
Upload SBOM Files
POST /api/sbom/upload
Upload one or more SBOM files for asynchronous processing.
Request: Multipart form-data.
Field |
Type |
Description |
|---|---|---|
|
file(s) |
One or more SBOM files |
|
UUID |
Target project |
|
UUID |
Target variant |
|
string |
Optional: |
Response: 202 Accepted
{
"upload_id": "...",
"scan_id": "...",
"message": "Upload accepted, processing started"
}
Check Upload Status
GET /api/sbom/upload/<upload_id>/status
Response:
{
"status": "processing",
"message": "Merging inputs..."
}
Status values: "processing", "done", "error".
Progress
NVD Progress
GET /api/nvd/progress
Get the current progress of NVD database updates.
Response:
{
"in_progress": true,
"phase": "Fetching CVE data",
"current": 42,
"total": 100,
"message": "Processing page 42/100",
"last_update": "2026-04-07T10:30:00Z",
"started_at": "2026-04-07T10:25:00Z"
}
EPSS Progress
GET /api/epss/progress
Get the current progress of EPSS score enrichment.
Response:
{
"in_progress": false,
"phase": "Complete",
"current": 100,
"total": 100,
"message": "EPSS enrichment complete",
"last_update": "2026-04-07T10:35:00Z",
"started_at": "2026-04-07T10:30:00Z"
}
Notifications
Get Notifications
GET /api/notifications
Returns pending system notifications (e.g. legacy setup warnings).
Response: JSON array — empty when no notifications are pending.
[
{
"level": "warning",
"title": "Legacy setup detected",
"message": "This container was started using the old docker-compose workflow.",
"action": "Run migration.sh to import your data."
}
]