SBOM Export API¶
Programmatic SBOM export in CycloneDX 1.5, SPDX 2.3, and STIX 2.1 formats.
Endpoints¶
All three endpoints share the same query parameters and auth. They differ only in the format of the returned body.
Authentication¶
Required. Uses the same session cookie as the rest of the user-facing SentriKat API. See Authentication.
Query parameters¶
| Parameter | Type | Required | Description |
|---|---|---|---|
organization | string | yes (or product) | Organization slug to scope the export |
product | string | no | Restrict to one product within the organization |
scan_id | uuid | no | Pin to a specific scan run (reproducible builds) |
include_dev | bool | no, default false | Include dev-only dependencies |
exclude_licenses | bool | no, default false | Strip SPDX license identifiers (smaller output) |
At least one of organization or product must be provided.
Response: CycloneDX¶
Content-Type: application/vnd.cyclonedx+json; version=1.5
{
"bomFormat": "CycloneDX",
"specVersion": "1.5",
"serialNumber": "urn:uuid:3e671187-...",
"version": 1,
"metadata": {
"timestamp": "2026-04-15T09:00:00Z",
"tools": [
{ "vendor": "SentriKat", "name": "SentriKat", "version": "2026.04.1" }
],
"component": {
"type": "application",
"name": "acme-backend",
"version": "1.2.3"
}
},
"components": [
{
"type": "library",
"name": "express",
"version": "4.18.2",
"purl": "pkg:npm/[email protected]",
"licenses": [{ "license": { "id": "MIT" } }]
}
],
"vulnerabilities": [
{
"id": "CVE-2024-12345",
"source": { "name": "the exploited-vulnerability catalogue" },
"ratings": [
{ "source": { "name": "EPSS" }, "score": 0.87, "method": "other" }
],
"affects": [{ "ref": "pkg:npm/[email protected]" }]
}
],
"integrity": {
"algorithm": "HMAC-SHA256",
"signature": "a1b2c3..."
}
}
Response: SPDX¶
Content-Type: application/spdx+json; version=2.3
{
"spdxVersion": "SPDX-2.3",
"dataLicense": "CC0-1.0",
"SPDXID": "SPDXRef-DOCUMENT",
"name": "acme-backend",
"documentNamespace": "https://sentrikat.com/sbom/acme/backend/20260415",
"creationInfo": {
"created": "2026-04-15T09:00:00Z",
"creators": ["Tool: SentriKat-2026.04.1"]
},
"packages": [
{
"SPDXID": "SPDXRef-Package-express",
"name": "express",
"versionInfo": "4.18.2",
"downloadLocation": "NOASSERTION",
"licenseConcluded": "MIT",
"licenseDeclared": "MIT",
"externalRefs": [
{
"referenceCategory": "SECURITY",
"referenceType": "cpe23Type",
"referenceLocator": "cpe:2.3:a:expressjs:express:4.18.2:*:*:*:*:*:*:*"
}
]
}
]
}
Response: STIX 2.1¶
Content-Type: application/stix+json; version=2.1
{
"type": "bundle",
"id": "bundle--3e671187-...",
"objects": [
{
"type": "software",
"spec_version": "2.1",
"id": "software--abc-...",
"name": "express",
"version": "4.18.2",
"vendor": "expressjs",
"cpe": "cpe:2.3:a:expressjs:express:4.18.2:*:*:*:*:*:*:*"
},
{
"type": "vulnerability",
"spec_version": "2.1",
"id": "vulnerability--def-...",
"name": "CVE-2024-12345",
"external_references": [
{ "source_name": "cve", "external_id": "CVE-2024-12345" }
]
}
]
}
Error responses¶
| Status | Reason |
|---|---|
| 400 | Missing both organization and product, or invalid scan_id |
| 401 | Missing or invalid session |
| 403 | The signed-in user does not have access to the requested scope |
| 404 | Organization or product not found |
| 409 | The findings or the filters changed between two parts of a findings export. Download all the parts again, from ?offset=0. |
| 413 | SBOM: scope too large (> 10 000 components), split by product. Findings exports: more rows than one file carries; download it in parts (below). |
| 500 | Schema validation failure (should never happen in normal operation) |
Rate limits¶
60 requests/minute per user. SBOM generation is O(N) in component count; exports of large orgs can take a few seconds.
Additional machine-readable exports¶
Beyond the SBOM formats above, SentriKat exports findings and vulnerability data in the standards your security pipeline already speaks:
| Format | Endpoint | Use |
|---|---|---|
| VEX (CycloneDX) | GET /api/vex/export/cyclonedx | Communicate exploitability/affected status of vulnerabilities (Vulnerability-Exploitability eXchange) |
| SARIF 2.1.0 | GET /api/findings/export/sarif | Ingest findings into code-scanning dashboards (e.g. GitHub Advanced Security) |
| CSAF 2.0 (OASIS) | GET /api/findings/export/csaf | Common Security Advisory Framework documents |
| ServiceNow VR | GET /api/findings/export/servicenow | Import into ServiceNow Vulnerability Response |
| Per-distro patch list | GET /api/findings/export/patch-list | Backport-aware, ready-to-apply patch list per Linux distribution |
All exports are scoped by organization or product and use the same session-cookie authentication as the SBOM endpoints.
Large findings exports come in parts¶
A findings export has a limit per file: 5,000 results for SARIF, 20,000 findings for ServiceNow, CSAF, DefectDojo, the device x CVE list and the remediation plan, 10,000 packages for the patch list. Above it, the export is never cut without telling you:
- Asked without
?offset=, it answers413with the total, the limit per part and how to ask for the parts. A file you download without asking for parts is always the whole export. - Asked with
?offset=0, it gives the first part, the most urgent rows first. Keep the same filters, and ask for the next part with theX-Export-Next-OffsetandX-Export-Snapshotheaders of each part as?offset=and?snapshot=, until a part has noX-Export-Next-Offset. - If the findings change between two parts (a sync ran), the next part answers
409: download all the parts again from?offset=0, so no row is skipped.
Every part carries these headers:
| Header | Meaning |
|---|---|
X-Export-Count | Rows in this part |
X-Export-Total | Rows in the whole export |
X-Export-Unit | What a row is: findings, updates or packages |
X-Export-Truncated | true when this file is one part of a larger export |
X-Export-Snapshot | A fingerprint of the order the parts are cut from |
X-Export-Next-Offset | Where the next part starts; absent on the last part |
A CSV part also says it in its file name, for example acme-device-cves-TRUNCATED-1-to-20000-of-21611-findings.csv. No marker row is added inside the file, because an importer would read it as a finding.
url="https://app.sentrikat.com/api/findings/export/defectdojo?organization=1"
offset=0; snapshot=""; n=1
while :; do
curl -sS -D headers.txt -b cookies.txt -o "part-$n.csv" \
"$url&offset=$offset${snapshot:+&snapshot=$snapshot}"
snapshot=$(grep -i '^x-export-snapshot:' headers.txt | tr -d '\r' | cut -d' ' -f2)
offset=$(grep -i '^x-export-next-offset:' headers.txt | tr -d '\r' | cut -d' ' -f2)
[ -z "$offset" ] && break
n=$((n + 1))
done
The Exports page in the product downloads every part for you.
See also¶
- SBOM Export user guide, UI workflow and integration examples
- Authentication, session and CSRF