Skip to content
engineer

Push SBOMs from CI

Was this page helpful?

Push an SBOM to a product version on every release, so your inventory reflects what actually shipped rather than what you remembered to re-export. This is two steps in any CI system: generate a CycloneDX file with Syft, then post it to the API.

Before you start

  • You need an API key scoped sboms:write. See API v1 reference for how keys are created and stored.
  • You need the product's ID, which you can copy from the product page.
  • Store the API key as a repository secret — never commit it to the workflow file.

Steps

  1. Add the API key as a repository secret, for example DECLARA_API_KEY.

  2. Add these steps to your release workflow, after the build:

    - name: Generate the SBOM
      run: |
        curl -sSfL https://raw.githubusercontent.com/anchore/syft/v1.18.1/install.sh \
          | sh -s -- -b "$RUNNER_TEMP" v1.18.1
        "$RUNNER_TEMP/syft" scan dir:. -o cyclonedx-json="$RUNNER_TEMP/sbom.json" -q
    
    - name: Upload it to Declara
      env:
        DECLARA_API_KEY: ${{ secrets.DECLARA_API_KEY }}
        PRODUCT_ID: your-product-id
        VERSION: ${{ github.ref_name }}
      run: |
        curl -sS --fail-with-body -X POST \
          "https://your-declara-host/api/v1/products/$PRODUCT_ID/sboms?version=$(jq -rn --arg v "${VERSION#v}" '$v|@uri')" \
          -H "Authorization: Bearer $DECLARA_API_KEY" \
          -H 'Content-Type: application/json' \
          --data-binary @"$RUNNER_TEMP/sbom.json"
  3. If your build already produces a CycloneDX or SPDX file, skip the first step and point --data-binary at that file instead.

  4. The version is the tag name without a leading v. The product version is created if it does not exist yet, so the workflow does not need to know whether this release has been seen before. It may be up to 100 characters of letters, digits and . _ + ~ : / @ -, starting with a letter or digit, so 1.2.3, 2026.10.01 and release/1.2-rc.1 all work. To make a retried upload safe, add -H "Idempotency-Key: $(uuidgen)" once per upload; see the API reference.

What you should see

A 202 Accepted response carrying an uploadId. The upload is accepted immediately and parses in the background — a large SBOM won't block your workflow waiting on matching to finish. Once parsing completes, check the product page in Declara for an updated component count, and Alerts for anything Tier A.

If the request is refused, the response body says why in words, for example a key without the sboms:write scope or a workspace that is read-only.

Limits

The upload endpoint accepts documents up to 4 MB from CI, and each API key may make 120 requests a minute. A larger SBOM needs to go through the browser upload flow instead, which writes straight to storage.