REST API
The Obexum portal exposes a small, stable HTTP API under
/api/v1 for reading your engagements, findings, and signed
reports, plus on-demand web and container scan modes. Every
/api/v1 endpoint authenticates with a personal API token
sent as a bearer credential.
https://obexum.com for the hosted service, or
your own host for a self-hosted deployment. Examples use
https://obexum.com.
Authentication
Each /api/v1 request is authenticated with an API token in
the Authorization header using the Bearer
scheme:
Authorization: Bearer obxk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Tokens are prefixed with obxk_ followed by a random,
URL-safe string. The portal stores only a SHA-256 hash of the token, so
the plaintext value is shown once, at creation time, and
cannot be recovered afterwards — if you lose it, revoke the token
and issue a new one.
A request with a missing, malformed, expired, or revoked token receives
401 Unauthorized with a JSON body:
{"error": "unauthorized", "docs": "https://obexum.com/docs/api"}
Browser sessions in the dashboard use cookie authentication; the same
/api/v1 endpoints also accept a valid session cookie as a
fallback, but bearer tokens are the supported path for programmatic and
CI use.
Managing API tokens
API tokens are created and revoked from the authenticated dashboard
(cookie session, not a bearer token). The token-management endpoints live
under /app/settings/api-tokens and back the API-tokens
screen in your account settings.
Create a token
POST/app/settings/api-tokens
Accepts a form-encoded body. Returns the new token's plaintext value exactly once.
| Field | Required | Notes |
|---|---|---|
name | yes | Human label for the token. |
scope | no | Zero or more scope values (repeat the field to send several). |
expires_days | no | Positive integer; expiry is set this many days out. Omit for no expiry. |
Response body:
{
"token": {
"id": "…",
"name": "ci-readonly",
"prefix": "obxk_ab12cd34",
"scopes": [],
"created_at": "2026-06-17T18:14:39Z"
},
"plaintext": "obxk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"warning": "Store this token immediately — it is shown only once."
}
List tokens
GET/app/settings/api-tokens
Returns { "tokens": [ … ] }. Each entry includes
id, name, prefix (the leading
characters of the plaintext, for display), scopes,
created_at, expires_at,
last_used_at, and use_count. Plaintext is never
returned here.
Revoke a token
POST/app/settings/api-tokens/{id}/revoke
Revokes the token identified by {id}. Returns
204 No Content on success.
Endpoints
The following endpoints are served under /api/v1 and require a bearer token.
| Method | Path | Purpose |
|---|---|---|
GET | /api/v1/engagements | List your engagements |
GET | /api/v1/engagements/{id}/findings | List findings for an engagement |
GET | /api/v1/engagements/{id}/findings/summary | Severity counts + compliance score |
GET | /api/v1/engagements/{id}/report.html | Signed report (HTML) |
GET | /api/v1/engagements/{id}/report.pdf | Signed report (PDF) |
GET | /api/v1/engagements/{id}/report.json | Signed report (JSON) |
GET | /api/v1/engagements/{id}/report.stix | Signed report (STIX 2.1) |
GET | /api/v1/engagements/{id}/report.stix.json | Signed report (STIX 2.1 JSON) |
POST | /api/v1/scan/web | Run a DAST web scan |
POST | /api/v1/scan/container | Scan a container image |
POST | /api/v1/scan/dockerfile | Static-analyze a Dockerfile |
POST | /api/v1/scan/sbom | Generate a CycloneDX SBOM for an image |
List engagements
GET/api/v1/engagements
Returns a JSON array of the engagements owned by the token's user (archived engagements excluded, newest first).
curl https://obexum.com/api/v1/engagements \
-H "Authorization: Bearer $OBEXUM_TOKEN"
Each element has this shape:
[
{
"id": "a3f1c2e4-…",
"name": "ACME prod RHEL9",
"description": "",
"target_os": "rhel-9",
"scan_profile": "default",
"status": "active",
"created_at": "2026-06-15T12:00:00Z",
"last_scan_at": "2026-06-16T09:31:00Z"
}
]
List findings
GET/api/v1/engagements/{id}/findings
Returns a JSON array of findings for the engagement, ordered by severity then score. Supports these query parameters:
| Param | Notes |
|---|---|
severity | Filter by severity (e.g. CRITICAL, HIGH, MEDIUM, LOW). |
status | One of open, fixed, suppressed, verifying. |
q | Case-insensitive substring match on rule_id or title. |
limit | Page size, 1–500 (default 100). |
offset | Row offset for pagination (default 0). |
curl "https://obexum.com/api/v1/engagements/a3f1c2e4-.../findings?severity=CRITICAL&status=open&limit=50" \
-H "Authorization: Bearer $OBEXUM_TOKEN"
Each finding has this shape (fields marked optional are omitted when empty):
[
{
"id": "…",
"rule_id": "AUD-WIN-ADCS-001",
"title": "AD CS template allows requester-supplied SAN",
"severity": "CRITICAL",
"status": "open",
"category": "identity",
"score_final": 9.8,
"kev": false,
"location_path": "…",
"location_service": "…",
"location_port": 0,
"first_seen_at": "2026-06-15T12:05:00Z",
"last_seen_at": "2026-06-16T09:31:00Z",
"tags": { },
"source": "audit",
"tier": "verified"
}
]
The source field names the producing engine
(audit is Obexum's validated check engine; advisory engines
such as lynis and cvematch also appear), and
tier is the derived confidence tier so verified findings are
distinguishable from advisory ones.
Findings summary
GET/api/v1/engagements/{id}/findings/summary
Returns aggregate counts and a compliance score for the engagement.
curl https://obexum.com/api/v1/engagements/a3f1c2e4-.../findings/summary \
-H "Authorization: Bearer $OBEXUM_TOKEN"
{
"engagement_id": "a3f1c2e4-…",
"counts": { /* counts per status bucket */ },
"compliance": 87,
"per_severity": { /* counts per severity */ }
}
Signed reports
GET/api/v1/engagements/{id}/report.{format}
Generates a report for the engagement. The format is selected by the path
suffix: .html, .pdf, .json,
.stix, or .stix.json. Every report is signed;
the integrity values are returned in response headers:
X-Obexum-Report-SHA256— SHA-256 of the document bodyX-Obexum-Report-Signature— signature over the documentX-Obexum-Report-Format— the rendered format
# Download the signed PDF and capture the integrity headers
curl -D headers.txt \
https://obexum.com/api/v1/engagements/a3f1c2e4-.../report.pdf \
-H "Authorization: Bearer $OBEXUM_TOKEN" \
-o acme-report.pdf
Web scan (DAST)
POST/api/v1/scan/web
Runs a dynamic scan against a live web target. The body is JSON;
target must be a full http(s):// URL. The
response is the scan result as JSON.
| Field | Required | Notes |
|---|---|---|
target | yes | Full http:// or https:// URL. |
max_depth | no | Crawl depth limit. |
max_urls | no | Maximum URLs to crawl. |
scan_timeout_seconds | no | Per-scan time budget, in seconds. |
user_agent | no | Override the request User-Agent. |
curl -X POST https://obexum.com/api/v1/scan/web \
-H "Authorization: Bearer $OBEXUM_TOKEN" \
-H "Content-Type: application/json" \
-d '{"target":"https://staging.example.com","max_depth":3,"max_urls":200}'
Container image scan
POST/api/v1/scan/container
Scans a container image for known vulnerabilities. Returns the scan result as JSON.
| Field | Required | Notes |
|---|---|---|
image | yes | Image reference (e.g. nginx:1.27). |
ignore_unfixed | no | When true, omit vulnerabilities with no available fix. |
curl -X POST https://obexum.com/api/v1/scan/container \
-H "Authorization: Bearer $OBEXUM_TOKEN" \
-H "Content-Type: application/json" \
-d '{"image":"nginx:1.27","ignore_unfixed":true}'
Dockerfile analysis
POST/api/v1/scan/dockerfile
Statically analyzes a Dockerfile. The request body is the raw Dockerfile
text (not JSON). Returns
{ "findings": [ … ], "count": N }.
curl -X POST https://obexum.com/api/v1/scan/dockerfile \
-H "Authorization: Bearer $OBEXUM_TOKEN" \
--data-binary @Dockerfile
SBOM export
POST/api/v1/scan/sbom
Generates a CycloneDX 1.5 JSON SBOM for a container image. The body is
JSON with a required image field; the response is the SBOM
with content type application/vnd.cyclonedx+json.
curl -X POST https://obexum.com/api/v1/scan/sbom \
-H "Authorization: Bearer $OBEXUM_TOKEN" \
-H "Content-Type: application/json" \
-d '{"image":"nginx:1.27"}' \
-o sbom.cdx.json
Errors
401 Unauthorized— missing, malformed, expired, or revoked token (JSON body witherror+docs).400 Bad Request— malformed id, invalid JSON, or a missing required field.404 Not Found— the engagement does not exist or is not owned by the token's user.500 Internal Server Error— an unexpected server-side failure.