> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pharussecurity.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Upload an SBOM

> POST /api/v1/sboms: send a CycloneDX or SPDX JSON SBOM for a product family from CI or any HTTP client.

<Info>
  **Plans:** Every plan. Refused with 402 while the workspace is read-only. **Who:** anyone with an API token for the product family; owners and admins create tokens.
</Info>

`POST /api/v1/sboms` stores one SBOM for the product family the token belongs to, then matches it within seconds. Send the same file again and Pharus answers with the SBOM it already has, so retries are safe.

```text theme={null}
POST https://app.pharussecurity.com/api/v1/sboms
```

## Example

```bash theme={null}
export PHARUS_TOKEN=phr_...   # Settings → API tokens

gzip -c sbom.cdx.json | curl --fail-with-body -sS -X POST "https://app.pharussecurity.com/api/v1/sboms" \
  -H "Authorization: Bearer $PHARUS_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Content-Encoding: gzip" \
  -H "X-Pharus-Version: 4.2" \
  --data-binary @-
```

`--fail-with-body` makes curl exit with an error on any 4xx or 5xx answer and still print Pharus' message, so a failed upload fails the CI step.

## Headers

| Header | Required | Value |
| - | - | - |
| `Authorization` | Yes | `Bearer phr_...`: an API token for the product family. See [authentication](/api-reference/introduction#authentication). |
| `Content-Type` | Recommended | `application/json`. Pharus recognises the format from the content. |
| `Content-Encoding` | When gzipped | `gzip`. Pharus also recognises a gzipped body without it. Any encoding other than `gzip`, `x-gzip` or `identity` is refused. |
| `X-Pharus-Version` | No | The product version this SBOM describes, such as `4.2`. Up to 100 characters. Overrides the version inside the SBOM. |
| `X-Pharus-Source` | No | Where the SBOM came from, shown as **Source**: `github`, `azure_devops`, `dependency_track`, `sbomify` or `api`. Anything else, or none, shows as API. |
| `X-Pharus-Commit` | No | The commit the SBOM was built from. Up to 80 characters. |
| `X-Pharus-Branch` | No | The branch. A leading `refs/heads/` is removed. |
| `X-Pharus-Repo` | No | The repository, such as `acme/gateway-g3`. |
| `X-Pharus-Run-Url` | No | A link to the CI run. Only `http` and `https` links are kept. |

The commit, branch, repository and run link show in the **Pipeline** card on the SBOM's page in Pharus.

## Query parameters

| Parameter | Value |
| - | - |
| `version` | The product version, used when there is no `X-Pharus-Version` header. Up to 100 characters. |

## Body

The raw SBOM file, plain or gzipped. Not a multipart form, and not wrapped in another JSON object.

* CycloneDX JSON 1.4, 1.5, 1.6 or 1.7, or SPDX JSON 2.2 or 2.3.
* At most 4 MB as sent, and 50 MB decompressed. Gzip keeps most SBOMs far below the first limit.
* At most 50 000 components.

Without a version in the header or the query, Pharus uses the product version in the SBOM. A new SBOM for a version replaces the active SBOM of that version in the family; SBOMs of other versions stay active. See [what happens after an SBOM arrives](/monitoring/sboms#what-happens-after-an-sbom-arrives).

## Responses

### 202 Accepted

A new SBOM was stored. Matching runs after the response.

```json theme={null}
{
  "id": "6f1c2a9e-4b7d-4e0a-9a51-2d3c8e7f1b40",
  "status": "parsed",
  "components": 19,
  "version": "4.2",
  "url": "https://app.pharussecurity.com/sboms/6f1c2a9e-4b7d-4e0a-9a51-2d3c8e7f1b40",
  "duplicate": false
}
```

### 200 OK

The family already has an SBOM with exactly these bytes. Nothing new was stored, and the response describes the SBOM Pharus has.

```json theme={null}
{
  "id": "6f1c2a9e-4b7d-4e0a-9a51-2d3c8e7f1b40",
  "status": "matched",
  "components": 19,
  "version": "4.2",
  "url": "https://app.pharussecurity.com/sboms/6f1c2a9e-4b7d-4e0a-9a51-2d3c8e7f1b40",
  "duplicate": true
}
```

| Field | Meaning |
| - | - |
| `id` | The SBOM's id in Pharus |
| `status` | `received`, `parsed`, `matched` or `failed`. A new SBOM is `parsed`: read, with matching under way. |
| `components` | The number of components Pharus read from this SBOM |
| `version` | The product version Pharus stored, or `null` when there is none |
| `url` | The SBOM's page in Pharus, for people who are signed in |
| `duplicate` | `true` when Pharus already had this file for the family |

### Errors

Errors return `{ "error", "message" }`, sometimes with `detail`. See the [error format](/api-reference/introduction#responses).

| Status | `error` | What to do |
| - | - | - |
| 400 | `invalid_json` | The body isn't valid JSON. Check that the file was written completely. |
| 400 | `invalid_sbom` | The format was recognised, but its structure is broken. Regenerate the SBOM. |
| 400 | `empty` | The body is empty. Check the file path and `--data-binary`. |
| 400 | `bad_encoding` | The body couldn't be decompressed. Send gzip with `Content-Encoding: gzip`, or plain JSON. |
| 401 | `unauthorized` | Check the token, its expiry and its status under **Settings → API tokens**. The response has a `WWW-Authenticate` header. |
| 402 | `billing_lapsed` | The workspace is read-only. An owner subscribes under **Settings → Billing**, or reopens a closed workspace. |
| 404 | `family_not_found` | The token's product family is no longer available. |
| 413 | `too_large` | Gzip the file, or split the product into smaller SBOMs. |
| 415 | `xml_unsupported` | Export JSON instead, for example `syft -o cyclonedx-json` or `cyclonedx convert --output-format json`. |
| 415 | `unsupported_format` | Send CycloneDX JSON 1.4–1.7 or SPDX JSON 2.2–2.3. `detail` names what Pharus found. |
| 422 | `too_many_components` | The SBOM lists more than 50 000 components. |
| 429 | `rate_limited` | Wait the number of seconds in `Retry-After`. Each token has 60 requests per hour. |
| 500 | `internal` | Try again. |

Pharus checks in this order: the token, the rate limit, the body's size and encoding, the workspace, whether the file is a duplicate, and then the SBOM itself.

## Related

<CardGroup cols={2}>
  <Card title="API introduction" icon="key-round" href="/api-reference/introduction" />

  <Card title="GitHub Actions" icon="git-branch" href="/api-reference/ci/github-actions" />

  <Card title="Azure Pipelines" icon="cloud" href="/api-reference/ci/azure-pipelines" />

  <Card title="GitLab CI" icon="git-merge" href="/api-reference/ci/gitlab-ci" />
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.