CBOM Documentation

CBOM Secure

  • Administrative Guide
  • Login and Authentication
    • Overview
    • Dashboard Interface
    • Dashboard Operations
    • Data Visualization
    • Overview
    • Host
    • Template
      • Networked Deployments
      • Cloud Deployments
      • Off-Grid Deployments
      • Tags
      • Integrations
      • Cloud Servers
      • CycloneDX Export
    • Sensors Reference
    • Overview
    • Summary Report
    • PQC Report
    • AI Services
    • Source Code Visualizer
    • Key
    • Certificate
    • CipherSuites
    • Analysis
    • Policy Management
    • User Policy
    • Standard Policy
    • User Management
    • Roles & Permissions
    • System Configuration
    • Introduction
      • Components
      • Data Flows
      • Execution Models
      • Incremental Scanning
      • Security Model
      • Quick Start
      • Topologies
      • Sizing Guide
      • HA and Scaling
      • HA Runbooks
      • Ports and Firewall
      • Reverse Proxy
      • Prerequisites
      • Docker Compose
      • Native on Linux
      • Deployment Server
      • Install Troubleshooting
      • Validation Checklist
      • Managing Services
      • Logging
      • Certificates
      • Configuration
      • Database Administration
      • Backup and Recovery
      • Upgrade and Migration
      • Ops Troubleshooting
      • Methods
      • SAML 2.0 SSO
      • OIDC and Entra ID
      • Token Authentication
      • Password Policy
      • Sensor vs Connector
      • Sensor Guide Structure
      • Capability Matrix
        • AWS
        • Azure
        • Google Cloud
        • Active Directory
        • LDAP Directories
        • AD CS
        • EJBCA
        • CertSecure Manager
        • Thales Luna HSM
        • Entrust nShield HSM
        • SoftHSM2
        • HashiCorp Vault KMIP
        • MSSQL TDE
        • Oracle TDE
        • MySQL TDE
        • MariaDB TDE
        • HTTPS and TLS
        • SSH Servers
        • Windows Certificate Store
        • Linux Trust Store
        • Java Keystores
        • PKCS#12 Keystores
        • GnuPG Keyrings
        • Binaries and Libraries
        • Source Code Scanning
        • GitHub
        • GitLab
        • Bitbucket
        • Local Git
      • Roadmap
      • Integration Model
      • CrowdStrike
      • ServiceNow
      • ServiceNow and Jira Setup
      • Notification Engine
    • Glossary
    • Configuration Reference
    • Data Model Reference
    • API Reference
    • API Authentication
    • Running a Scan via API
    • Searching via API
    • CycloneDX Export Specification
    • Risk and PQC Scoring
    • Built-in Standards
    • Widget Catalogue
    • AI Services Data Handling

Running a Scan Through the API

Everything the console does when it runs a discovery scan is available over the API. This page is the end-to-end sequence: authenticate, assemble what the scan needs, run it, find out when it finished and read what it found.

The route catalogue is on the API Reference page and the session model is on the API Authentication page. This page is about the order they go in.

The model in one paragraph

A template holds sensor configuration — which sensors to run and with what settings. A task binds one or more templates to a set of hosts. A deployment is one run of a task. Scanning through the API therefore means creating or finding a template, creating a task that points at it and at some hosts, and then posting a deployment. Nothing is scanned by naming a target directly; the target has to be a host the platform knows about, because that is what results are attributed to.

Step 1 — Authenticate

POST /cbomsrv/api/v1/auth/login
Content-Type: application/json

{ "username": "svc-automation", "password": "..." }

The response carries a session_id. Send it as a bearer token on every subsequent request:

Authorization: Bearer <session_id>

Sessions are short-lived. A long-running integration should call POST /auth/auto-renew periodically rather than logging in again each time — see API Authentication for the renewal rules.

Use a dedicated service account with the narrowest role that works. Deploying a task needs admin, manager or user. Reading results needs less. An integration that only exports does not need an account that can deploy.

Step 2 — Find the hosts

GET /cbomsrv/api/v1/hosts/

Each host carries a hostId, which is what a task refers to. To add a host as part of the automation:

POST /cbomsrv/api/v1/hosts/
Content-Type: application/json

{
  "hostName": "app01.corp.example.com",
  "ipAddress": "10.20.30.41",
  "osType": "linux",
  "credentials": { "username": "cbom-agent", "password": "..." }
}

The response returns the allocated hostId. Credentials are encrypted on write and are never returned by any read — a subsequent GET shows a mask, and posting that mask back does not overwrite the stored value.

For a bulk load, POST /hosts/import-csv/ runs as a background job and GET /hosts/import-csv/status/{jobId}/ reports its progress.

Step 3 — Find or create a template

GET /cbomsrv/api/v1/template/

To learn what a sensor accepts before configuring it:

GET /cbomsrv/api/v1/template/schemas/list/
GET /cbomsrv/api/v1/template/schemas/?name=Discover_SSH

The schema is the authoritative field list for that sensor in that deployment — it is what the console's own form is built from, so a template assembled against it cannot carry a field the sensor will reject.

Creating a template:

POST /cbomsrv/api/v1/template/define/
Content-Type: application/json

{
  "templateName": "ssh-estate",
  "sensors": [
    {
      "ExtName": "Discover_SSH",
      "Hosts": ["bastion.corp.example.com", "jump02.corp.example.com"],
      "timeout_seconds": 15
    }
  ]
}

A cloned template lands in draft, deliberately. POST /template/clone/ never produces a finalised template, because an unreviewed copy that was immediately assignable to hosts is how a half-edited configuration reaches production. Activate it explicitly with POST /template/activate/.

Step 4 — Create the task

POST /cbomsrv/api/v1/tasks/assign-hosts/
Content-Type: application/json

{
  "taskName": "nightly-ssh",
  "templateIds": ["<templateId>"],
  "hosts": ["10001", "10002"],
  "tags": [3, 7]
}

tags are optional and are integer ids from GET /cmtags/. They propagate onto every material the scan produces, which is what makes a scoped dashboard possible later — so it is worth setting them here rather than trying to attribute results afterwards.

Step 5 — Run it

POST /cbomsrv/api/v1/deployment/
Content-Type: application/json

{ "taskId": "<taskId>" }

This is the call that does the work. It deploys the agent where one is needed, runs the sensors, and returns when the run has been dispatched and accounted for.

Read the counters in the response, not just the status code. The response reports, per host, how many documents were requested, inserted, updated and skipped, alongside any per-host errors. A 200 means the deployment ran, not that everything landed:

CounterMeaning
requested_countDocuments the sensors produced
inserted_countNew materials written
updated_countExisting materials refreshed
skipped_countDocuments that failed validation and were discarded

skipped_count is the one to watch. A document that fails to upsert leaves no other trace — this counter is the only record that it existed. A run with a non-zero skipped count has lost discovery data, and the cause is in the ingestion log.

A deployment that succeeded on some hosts and failed on others returns 200 with the failures named in the response. It is not a partial HTTP status; read the body.

Step 6 — Track status

There is no single "scan status" endpoint, because a run is distributed across hosts and transports. Three reads cover it:

ReadAnswers
GET /tasks/statusThe rollup across tasks — what is running now
GET /deployment/history/?taskId=<id>The runs of one task, newest first, with the same four counters and any errors
GET /ui/utils/lastCMDocsInsertTimeWhen the inventory last changed, as a cheap freshness check

GET /deployment/history/ is the one to poll. It records only the ingestion facts — the deployment ids, the errors, the counters and the time. It deliberately does not record the live agent tokens the deployment response carries.

Results arrive asynchronously. Agents publish findings to the message broker and the collector writes them as they land, so the inventory continues to fill after the deployment call has returned. Polling the deployment history is how you learn that it finished; polling immediately after the deploy call will show less than the run eventually produces.

Being told instead of polling

There is no per-scan callback registration — you do not hand the platform a URL when you start a scan. Scan lifecycle is handled by the notification engine instead, which is configured once and applies to every run.

GET /cbomsrv/api/v1/notification/catalog/events

The events relevant to a scan:

EventRaised when
task.startedA task's run begins
task.completedA task's run finishes
task.failedA task's run fails
deploy.host.succeededOne host in the run completed
deploy.host.failedOne host in the run failed
cm.collect.completedA collection finished landing in the inventory
cm.collect.failedA collection failed
dedup.run.completedThe deduplication pass that resolves and rolls up materials finished
cm.material.createdA material appeared that was not there before
cm.material.resolvedA material stopped being seen and was resolved

Destinations are registered with POST /notification/destinations and verified with POST /notification/destinations/{id}/verify. Supported outputs are in-app, email, Microsoft Teams, Jira and ServiceNow; GET /notification/catalog/outputs returns what the deployment actually has. A Teams destination takes an incoming webhook URL, which is the closest equivalent to a callback: a rule on task.completed with a Teams destination posts to your URL on every run.

Delivery is tracked — GET /notification/outbox shows what was sent and what failed, and POST /notification/outbox/{id}/retry resends.

Reading what the scan found

ReadReturns
GET /cyclonedx/export?...The materials themselves, as CycloneDX 1.6
GET /cmhistory/What changed across the inventory, as a feed
GET /analytic/host-analysis/The per-host risk rollup
POST /ui/dashboard/widgets/{wid}/dataAny dashboard figure, under your own parameters

Searching and filtering are covered on the Searching the Inventory page.

GET /cyclonedx/export requires a time bound. The all-types export will not run unbounded — an unbounded export of a large estate is a request that times out and takes resources with it. The per-type exports accept the same filters and are the better choice for an integration that syncs one material type.

A complete worked example

Running an existing task nightly and failing the job if anything was skipped:

BASE=https://cbom.example.com/cbomsrv/api/v1

SID=$(curl -s -X POST "$BASE/auth/login" \
  -H 'Content-Type: application/json' \
  -d '{"username":"svc-automation","password":"'"$CBOM_PASSWORD"'"}' \
  | jq -r .data.session_id)

RUN=$(curl -s -X POST "$BASE/deployment/" \
  -H "Authorization: Bearer $SID" \
  -H 'Content-Type: application/json' \
  -d '{"taskId":"'"$TASK_ID"'"}')

echo "$RUN" | jq '.data'

SKIPPED=$(echo "$RUN" | jq '[.data[]?.skipped_count // 0] | add // 0')
if [ "$SKIPPED" -gt 0 ]; then
  echo "WARNING: $SKIPPED document(s) were rejected during ingestion" >&2
  exit 1
fi

curl -s -X GET "$BASE/auth/logout" -H "Authorization: Bearer $SID" > /dev/null

Log out when the automation finishes. A session left open stays valid until it expires, and a service account's session is worth as much as its password.

Common problems

401 on every call after the first. The session identifier is being sent as something other than a bearer token, or the session has expired. Sessions are short-lived by design; renew rather than re-authenticating in a loop.

403 on the deploy call. The account's role does not carry the deployment permission. Authorisation is enforced server-side from the Roles and Permissions page, so a role that can see the button in the console is not necessarily a role that can make this call — and the reverse is not true either.

404 on the deploy call. The taskId does not name a task. A task id from a different environment is the usual cause.

200 with zero inserted and zero updated. The scan ran and found nothing new. Where that is unexpected, check the per-host errors in the response before suspecting the sensor — an agent that could not reach its target reports here.

The inventory does not change after a successful deploy. Results travel through the broker, so a broker that is not consuming leaves them queued. Check the collector worker, and check skipped_count for documents the validator rejected.

On this Page:
Support Privacy Policy Terms of Service

Copyright 2026 Encryption Consulting LLC. All rights reserved.