Skip to content

Bills of materials

Part of the API reference. Authentication, scopes, rate limits and errors are on the overview.

BitHive Reporting Desk API · 0.11.0

Bills of materials

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

ParameterInTypeDescription
productId requiredpathstring (uuid)Identifier of the product.

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

ParameterInTypeDescription
productId requiredpathstring (uuid)Identifier of the product.
releasequerystringThe release the document describes, as your customers know it, for example 2.4.1. Without it, the version the document gives its own subject is taken (CycloneDX `metadata.component.version`, or the one package an SPDX document describes).
on_marketquerybooleanTrue when this release is on the market, so it stays monitored after newer uploads. False, or leaving it out, changes nothing; take a release off the market with `PATCH /v1/sboms/{sbomId}`.
Idempotency-Keyheaderstring1 to 255 visible ASCII characters, new for each write and the same when the write is sent again. The same key sent again by the same token within 24 hours is answered with the first answer and does nothing twice; with another request it is refused (422), and while the first request still runs (409). Only a successful answer is kept, so a refused write can be corrected and sent with the same key. A record, a report or a statement written is never written again under its key: when the first request's answer failed after the write, the same request sent again is answered with what was written, or with 409 while that answer is being settled.

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/json
  • application/vnd.cyclonedx+json
  • application/spdx+json
  • multipart/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
}

201 The document was stored. SBOM

Example

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"

GET/v1/sboms/{sbomId}

Read one upload

Returns the upload. An upload of another account, and one belonging to a product the token is not limited to, are reported as not found. Requires the scope sboms:read.

Scope: sboms:read

Parameters

ParameterInTypeDescription
sbomId requiredpathstring (uuid)Identifier of the upload.

Responses

200 The upload. SBOM

Example

Example

curl --fail-with-body -sS \
  -H "Authorization: Bearer $BITHIVE_TOKEN" \
  "https://app.bithive-it.com/v1/sboms/$SBOMID"

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

ParameterInTypeDescription
sbomId requiredpathstring (uuid)Identifier of the upload.
Idempotency-Keyheaderstring1 to 255 visible ASCII characters, new for each write and the same when the write is sent again. The same key sent again by the same token within 24 hours is answered with the first answer and does nothing twice; with another request it is refused (422), and while the first request still runs (409). Only a successful answer is kept, so a refused write can be corrected and sent with the same key. A record, a report or a statement written is never written again under its key: when the first request's answer failed after the write, the same request sent again is answered with what was written, or with 409 while that answer is being settled.

Request body

  • application/json
{
  "on_market": true
}

Responses

200 The upload as it is now. SBOM

Example

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

ParameterInTypeDescription
sbomId requiredpathstring (uuid)Identifier of the upload.

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

ParameterInTypeDescription
sbomId requiredpathstring (uuid)Identifier of the upload.

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"

The contract itself. This page is built from the OpenAPI document of the service. It is published as a file, so a client generator can read the same contract this page describes. Download openapi.yaml

Docs

Start free 14-day trial