GET/v1/products/{productId}/sboms
List the uploads of one product
Returns the product's uploads, newest first, each with whether it is on the
market and whether it is monitored. Requires the scope sboms:read.
Scope: sboms:read
Parameters
Responses
200 The uploads of the product. SBOMList
{
"sboms": [
{
"id": "5d7e9f10-2b4c-4d6e-8f90-a1b2c3d4e5f6",
"product_id": "3f1b0c9e-6a27-4f5b-9c1d-2e8a7b4d5c60",
"format": "cyclonedx",
"spec_version": "1.6",
"sha256": "9f2c4a7b1e3d5f60829a4b6c8d0e1f2a3b4c5d6e7f80910a2b3c4d5e6f708192",
"component_count": 412,
"source": "api",
"created_at": "2026-09-18T06:31:10Z",
"release": "2.4.1",
"on_market": true,
"monitored": true
}
]
}
Example
curl --fail-with-body -sS \
-H "Authorization: Bearer $BITHIVE_TOKEN" \
"https://app.bithive-it.com/v1/products/$PRODUCTID/sboms"
POST/v1/products/{productId}/sboms
Upload a bill of materials for a product
Stores a CycloneDX or SPDX document in JSON, at most 10 MiB, and indexes
its components. The format is read from the document, not from the
content type. Uploading the same bytes for the same product again answers
200 with the existing upload instead of storing a second one, so a build
pipeline that runs twice on an unchanged tree is harmless.
Every release on the market is monitored, not only the latest upload
(Cyber Resilience Act, Article 14). Name the release with release and say
it is on the market with on_market=true, and it stays monitored after newer
uploads. The latest upload of a product is always monitored. Up to 25
releases of a product can be on the market, in every plan; they do not count
toward the product limit. An upload is never refused for that limit: past it,
the document is stored and on_market_refused is true. When the same document
is uploaded again, a release or on_market=true given with it still applies;
on_market=false changes nothing (take a release off the market with
PATCH /v1/sboms/{sbomId}).
No bill of materials? Send the lockfiles or manifests your build already has as
multipart/form-data, each file in a part named file (API 0.9.0): package-lock.json
(npm), poetry.lock or a requirements.txt with exact versions (PyPI), go.mod with
its go.sum (Go), packages.lock.json (NuGet), or a pom.xml (Maven). The Reporting Desk writes one
CycloneDX document from them on its own servers and stores it like an uploaded one,
with generated_from naming the files. The files are only read: nothing is installed,
fetched or run, and the files themselves are not kept. A pom.xml names no transitive
dependency, a go.mod for a Go before 1.17 names only the direct ones, a requirements.txt names them only when every pinned line carries a
--hash, as pip-compile --generate-hashes writes it (pip then installs nothing the
file does not name), and a requirements.txt line without an exact version names no version: the document
is then marked partial, with left_out counting what had no exact version. At most 20 files of 2 MiB each, and at most 10 documents written an hour for
an account (429 past that; the same files again store nothing new and always pass).
The Reporting Desk writes at most two such documents at once, and one per account: a
request beyond that is refused before any of it is read, 429 while another request of
the account is being written and 503 while others are, each with Retry-After in
seconds. A request for a product the account does not have, or over the hourly limit for
a product with no document written from lockfiles yet, is refused before it is read too.
The same files under the same names give the same document, so sending them again
answers 200.
Requires the scope sboms:upload, which uploads and does nothing else, or
sboms:write. Takes an Idempotency-Key.
Scope: sboms:write
Parameters
Request body
The document itself, as sent by the build tool; or lockfiles and manifests as
`multipart/form-data`, each in a part named `file`.
application/jsonapplication/vnd.cyclonedx+jsonapplication/spdx+jsonmultipart/form-data
{
"bomFormat": "CycloneDX",
"specVersion": "1.6",
"components": [
{
"name": "openssl",
"version": "3.0.8",
"purl": "pkg:generic/openssl@3.0.8"
},
{
"name": "zlib",
"version": "1.2.13"
}
]
}
Responses
200 The same document was already stored for this product; nothing new was
stored, but a `release` or `on_market=true` given with it was applied.
SBOM
{
"id": "5d7e9f10-2b4c-4d6e-8f90-a1b2c3d4e5f6",
"product_id": "3f1b0c9e-6a27-4f5b-9c1d-2e8a7b4d5c60",
"format": "cyclonedx",
"spec_version": "1.6",
"sha256": "9f2c4a7b1e3d5f60829a4b6c8d0e1f2a3b4c5d6e7f80910a2b3c4d5e6f708192",
"component_count": 412,
"source": "api",
"created_at": "2026-09-18T06:31:10Z",
"release": "2.4.1",
"on_market": true,
"monitored": true
}
400 The document is empty, not JSON, in neither format, or lists something unusable. Problem
{
"type": "about:blank",
"title": "Bad Request",
"status": 400,
"detail": "The document is not JSON."
}
409 A request with the same Idempotency-Key is still running. Problem
Example
415 The content type is not one of the accepted JSON types. Problem
{
"type": "about:blank",
"title": "Unsupported Media Type",
"status": 415,
"detail": "Send the document as application/json, application/vnd.cyclonedx+json or application/spdx+json."
}
422 The release name is longer than 100 characters or holds a line break or another control character. Problem
{
"type": "about:blank",
"title": "Unprocessable Entity",
"status": 422,
"detail": "A release name is at most 100 characters, on one line."
}
503 Lockfiles only: the Reporting Desk is writing as many documents from lockfiles as it
does at once. Nothing was read or stored; send the files again after `Retry-After`.
Problem
Example
Example
curl --fail-with-body -sS -X POST \
-H "Authorization: Bearer $BITHIVE_TOKEN" \
-H "Content-Type: application/vnd.cyclonedx+json" \
--data-binary @sbom.json \
"https://app.bithive-it.com/v1/products/$PRODUCTID/sboms"
PATCH/v1/sboms/{sbomId}
Name a release, or put it on the market or take it off
Changes what is said about the release an upload describes: its name, and
whether it is on the market. A release on the market is monitored after
newer uploads; one taken off stops opening new cases, and the cases already
open stay. A field left out stays as it is. Up to 25 releases of a product
can be on the market; past that the change is refused with 409, and nothing
of it is applied. Every change is recorded in the account's audit log as the
token's. Requires the scope sboms:write. Takes an Idempotency-Key.
Scope: sboms:write
Parameters
Request body
{
"on_market": true
}
Responses
400 The document is empty, not JSON, in neither format, or lists something unusable. Problem
Example
409 The product already has the most releases on the market it may have. Problem
{
"type": "about:blank",
"title": "Conflict",
"status": 409,
"detail": "This product already has 25 releases on the market. Take one off first, with \"on_market\": false."
}
415 The content type is not one of the accepted JSON types. Problem
Example
422 The release name is longer than 100 characters or holds a line break or another control character. Problem
Example
Example
curl --fail-with-body -sS -X PATCH \
-H "Authorization: Bearer $BITHIVE_TOKEN" \
-H "Content-Type: application/json" \
--data-binary @release-change.json \
"https://app.bithive-it.com/v1/sboms/$SBOMID"
GET/v1/sboms/{sbomId}/components
Read the component index of one upload
Returns every component the document listed, by name. The list is
complete and needs no paging: a document may not carry more than 50,000
components. Requires the scope sboms:read.
Scope: sboms:read
Parameters
Responses
200 The components of the upload. ComponentList
{
"components": [
{
"name": "golang.org/x/net",
"version": "0.38.0",
"purl": "pkg:golang/golang.org/x/net@0.38.0"
},
{
"name": "github.com/jackc/pgx/v5",
"version": "5.7.6",
"purl": "pkg:golang/github.com/jackc/pgx/v5@5.7.6"
}
],
"total": 412
}
Example
curl --fail-with-body -sS \
-H "Authorization: Bearer $BITHIVE_TOKEN" \
"https://app.bithive-it.com/v1/sboms/$SBOMID/components"
GET/v1/sboms/{sbomId}/document
Download the document as it was uploaded
Returns the stored bytes unchanged, which is what an audit or a report
attachment needs. Requires the scope sboms:read.
Scope: sboms:read
Parameters
Responses
200 The document, byte for byte as uploaded.
{
"bomFormat": "CycloneDX",
"specVersion": "1.6",
"serialNumber": "urn:uuid:1b2c3d4e-5f60-4a7b-8c9d-0e1f2a3b4c5d",
"version": 1,
"components": [
{
"type": "library",
"name": "golang.org/x/net",
"version": "0.38.0",
"purl": "pkg:golang/golang.org/x/net@0.38.0"
}
]
}
Example
curl --fail-with-body -sS \
-H "Authorization: Bearer $BITHIVE_TOKEN" \
"https://app.bithive-it.com/v1/sboms/$SBOMID/document"