본문으로 건너뛰기

TRUSCA API (2.2.0)

Download OpenAPI specification:Download

Open-source self-hosted SCA portal — CVE, license compliance, and SBOM management with EPSS prioritization, VEX consumption, CI build gating, and Trivy-backed CVE matching with weekly DB refresh + automatic re-matching on new vulnerability data.

auth

Request a password reset link (public, rate limited)

Public — no authentication required, but limited per PASSWORD_RESET_RATE_LIMIT (5/min/IP by default).

Body shape: {"email": "<address>"}. Always returns 204 + empty body (CWE-204). When the address matches a registered user we additionally enqueue an email via Celery. When the per-email cooldown is active we set Retry-After to the configured cooldown and STILL return 204.

Authorizations:
bearerAuth
Request Body schema: application/json
required
email
required
string <email> (Email)

Responses

Request samples

Content type
application/json
{
  • "email": "user@example.com"
}

Response samples

Content type
application/problem+json
{
  • "detail": "string",
  • "instance": "string",
  • "status": 0,
  • "title": "string",
  • "type": "about:blank"
}

Login (public, rate limited)

Public — no authentication required, but limited to 5 attempts/minute/IP.

On success: 200 + access_token in the body, refresh as HttpOnly cookie. On bad credentials: 401 problem+json.

Request Body schema: application/json
required
email
required
string <email> (Email)
password
required
string (Password) [ 1 .. 256 ] characters

Responses

Request samples

Content type
application/json
{
  • "email": "user@example.com",
  • "password": "string"
}

Response samples

Content type
application/json
{
  • "access_token": "string",
  • "expires_in": 0,
  • "token_type": "bearer"
}

Logout (revoke refresh cookie)

Revoke the refresh cookie. Idempotent — always returns 204 even if the cookie is absent or already revoked.

Authorizations:
bearerAuth
cookie Parameters
Refresh Token (string) or Refresh Token (null) (Refresh Token)

Responses

Response samples

Content type
application/problem+json
{
  • "detail": "string",
  • "instance": "string",
  • "status": 0,
  • "title": "string",
  • "type": "about:blank"
}

Return the currently authenticated user and their memberships

Authenticated. UserPublic + the caller's team memberships.

The frontend reads memberships to resolve a team_id for project creation / write scoping. Ordered oldest-first so memberships[0] is a stable default team.

Authorizations:
bearerAuth

Responses

Response samples

Content type
application/json
{
  • "created_at": "2019-08-24T14:15:22Z",
  • "email": "string",
  • "full_name": "string",
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "is_active": true,
  • "is_superuser": true,
  • "memberships": [
    ],
  • "mfa_enabled": false
}

Finish a sign-in by supplying the second factor (public, rate limited)

Public. The pending token from POST /auth/login is the credential, and it is worth nothing on its own.

On success: 200 + access_token, refresh as an HttpOnly cookie. This is the first point at which a session exists.

Authorizations:
bearerAuth
Request Body schema: application/json
required
code
required
string (Code) [ 1 .. 64 ] characters
mfa_token
required
string (Mfa Token) [ 1 .. 4096 ] characters

Responses

Request samples

Content type
application/json
{
  • "code": "string",
  • "mfa_token": "string"
}

Response samples

Content type
application/json
{
  • "access_token": "string",
  • "expires_in": 0,
  • "token_type": "bearer"
}

List OAuth provider availability (public)

Public — no authentication required (explicit exception to CLAUDE.md core rule #12: the consumer is the anonymous /login page, which must decide which OAuth sign-in buttons to render BEFORE any credential exists).

Always lists every supported provider with a bare configured boolean. configured is True only when both the client id AND client secret are set — the same condition under which /{provider}/authorize actually works (M-15: a half-configured or unconfigured provider previously surfaced as a rendered button that 503'd on click).

Security: the response carries booleans only — never client ids, secrets, or any other configuration detail.

Responses

Response samples

Content type
application/json
{
  • "providers": [
    ]
}

Begin OAuth sign-in (public, rate limited)

Public — no authentication required.

302 → provider's authorize URL with a signed state. Returns:

  • 503 + oauth_provider_disabled Problem Details when the provider's client id/secret is not configured.
  • 404 Problem Details when the provider name is unknown (FastAPI's Literal[] gate normally catches this; the explicit branch survives future enum widening).
  • 403 demo_read_only Problem Details when the deployment runs in read-only live-demo mode (OAuth sign-in is a write; see :func:_demo_read_only_blocked).
  • 429 when an IP exceeds OAUTH_AUTHORIZE_RATE_LIMIT. Looser than the login limit on purpose: there is no credential to guess here, and an office behind one NAT address signs in together. The limit is a backstop; what actually bounds the cost of a cold discovery cache is the provider's own failure cache.
path Parameters
provider
required
string (Provider)
Enum: "github" "google" "oidc"
query Parameters
Redirect After (string) or Redirect After (null) (Redirect After)

Responses

Response samples

Content type
application/json
null

OAuth callback (public)

Public — the provider's redirect lands here after consent.

Success path: 302 → redirect_after (or configured default) with the refresh-token HttpOnly cookie attached.

Failure path: 302 → configured failure URL with ?error=<reason>. The provider's own ?error=access_denied (user clicked "Cancel") falls through here too — we forward a normalised error=oauth_denied.

Read-only demo: if DEMO_READ_ONLY is enabled we 403 BEFORE any token exchange or DB write (see :func:_demo_read_only_blocked), so the callback can never create/link a User or Team in the demo.

path Parameters
provider
required
string (Provider)
Enum: "github" "google" "oidc"
query Parameters
Code (string) or Code (null) (Code)
State (string) or State (null) (State)
Error (string) or Error (null) (Error)

Responses

Response samples

Content type
application/json
null

Rotate refresh token (public; refresh cookie is the credential, rate limited)

Public — the refresh cookie is the credential.

Successful rotation: 200 + new access_token + new refresh cookie. Reuse detected (cookie already rotated): 401, entire chain revoked.

cookie Parameters
Refresh Token (string) or Refresh Token (null) (Refresh Token)

Responses

Response samples

Content type
application/json
{
  • "access_token": "string",
  • "expires_in": 0,
  • "token_type": "bearer"
}

Register a new user (public, rate limited)

Public, no authentication required.

Limited to REGISTRATION_RATE_LIMIT (5/min/IP by default): the bcrypt password hash is CPU-bound, so an uncapped public endpoint could occupy the worker's event loop with unauthenticated requests.

Returns the new user (without password). 422 for validation errors, 409 if the email is already registered, and 404 when the deployment maintains its roster itself (AUTH_SELF_REGISTRATION=false), which is the same answer an outsider gets for any route that is not there.

Request Body schema: application/json
required
email
required
string <email> (Email)
Full Name (string) or Full Name (null) (Full Name)
password
required
string (Password) [ 8 .. 256 ] characters

At least 8 characters (NIST 800-63B minimum), not a common password.

Responses

Request samples

Content type
application/json
{
  • "email": "user@example.com",
  • "full_name": "string",
  • "password": "stringst"
}

Response samples

Content type
application/json
{
  • "created_at": "2019-08-24T14:15:22Z",
  • "email": "string",
  • "full_name": "string",
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "is_active": true,
  • "is_superuser": true
}

Confirm a password reset using a one-shot token (public, rate limited)

Public — the reset token is the credential.

Limited to PASSWORD_RESET_CONFIRM_RATE_LIMIT (5/min/IP by default, same as login): the token is guessable the same way a password is, and the verify path costs a bcrypt call per live candidate, so this endpoint needs no less protection than login (F1, concurrency-scaling-plan-2026-08-22.md).

On success: 204 + every refresh token for the user is revoked. On bad / expired / used token: 422 problem+json.

Authorizations:
bearerAuth
Request Body schema: application/json
required
new_password
required
string (New Password) [ 8 .. 256 ] characters

At least 8 characters (NIST 800-63B minimum), not a common password.

token
required
string (Token) [ 8 .. 256 ] characters

Responses

Request samples

Content type
application/json
{
  • "new_password": "stringst",
  • "token": "stringst"
}

Response samples

Content type
application/problem+json
{
  • "detail": "string",
  • "instance": "string",
  • "status": 0,
  • "title": "string",
  • "type": "about:blank"
}

public

Liveness probe — PUBLIC, unauthenticated

Cheap PURE-LIVENESS probe used by docker-compose / k8s liveness checks.

PUBLIC / unauthenticated (CLAUDE.md rule #12 explicit exception). This proves only that the uvicorn process is accepting requests — it does NOT touch the database and says nothing about schema state. For "is the schema migrated and safe to serve traffic / start workers", use GET /health/ready (api/v1/health.py).

v2.1 Track B (B5): also surfaces demo_read_only so the SPA can render the read-only banner and disable write actions without needing a separate build. The flag is resolved at request time (CLAUDE.md rule #11), so the same image behaves correctly whether DEMO_READ_ONLY is set or not.

Also surfaces demo_sandbox_scans — only true when the read-only demo has the opt-in sandbox carve-out enabled (both DEMO_READ_ONLY and DEMO_ALLOW_SANDBOX_SCANS). The SPA uses it to re-enable the bounded scan / SBOM-ingest affordances on the Demo Sandbox project while keeping every other write disabled.

Responses

Response samples

Content type
application/json
{ }

Readiness probe (schema at Alembic HEAD) — PUBLIC, unauthenticated

Return 200 when the DB schema matches the Alembic HEAD, else 503.

PUBLIC: no auth dependency by design (probe endpoint — see module docstring and CLAUDE.md core rule #12). The check is read-only (a single SELECT on alembic_version plus an in-image read of the script tree).

The redis field is observational only (issue #399): it never changes the status code, so a Redis outage does not pull the backend out of an orchestrator's rotation for a dependency the request path already fails open through. See core.readiness for the reasoning.

Responses

Response samples

Content type
application/json
{
  • "redis": "ok",
  • "status": "ready"
}

metrics

Operational metrics, in the Prometheus text format

Responses

about

Product identity and the list of license notices

Return what this deployment is, and which notices it can show.

Authorizations:
bearerAuth

Responses

Response samples

Content type
application/json
{}

Read one license-notice document

Return one notice document as text/plain.

Verbatim: a license text that has been reflowed, truncated or markdown-rendered is no longer the notice it is standing in for.

Authorizations:
bearerAuth
path Parameters
document_id
required
string (Document Id)

Responses

Response samples

Content type
application/problem+json
{
  • "detail": "string",
  • "instance": "string",
  • "status": 0,
  • "title": "string",
  • "type": "about:blank"
}

admin

Active API-key count by stored-hash format (bcrypt vs HMAC-SHA256, A5)

Authorizations:
bearerAuth

Responses

Response samples

Content type
application/json
{
  • "active_total": 0,
  • "hmac_sha256_count": 0,
  • "legacy_bcrypt_count": 0
}

Active API-key count by stored-hash format (bcrypt vs HMAC-SHA256, A5)

Authorizations:
bearerAuth

Responses

Response samples

Content type
application/json
{
  • "active_total": 0,
  • "hmac_sha256_count": 0,
  • "legacy_bcrypt_count": 0
}

Search audit log (admin) — paginated, filterable

Authorizations:
bearerAuth
query Parameters
Actor User Id (string) or Actor User Id (null) (Actor User Id)
Target Table (string) or Target Table (null) (Target Table)
Action (string) or Action (null) (Action)
From (string) or From (null) (From)
To (string) or To (null) (To)
Q (string) or Q (null) (Q)
page
integer (Page) [ 1 .. 1000000 ]
Default: 1
page_size
integer (Page Size) [ 1 .. 200 ]
Default: 50

Responses

Response samples

Content type
application/json
{
  • "has_more": true,
  • "items": [
    ],
  • "page": 1,
  • "page_size": 1,
  • "total": 0
}

Search audit log (admin) — paginated, filterable

Authorizations:
bearerAuth
query Parameters
Actor User Id (string) or Actor User Id (null) (Actor User Id)
Target Table (string) or Target Table (null) (Target Table)
Action (string) or Action (null) (Action)
From (string) or From (null) (From)
To (string) or To (null) (To)
Q (string) or Q (null) (Q)
page
integer (Page) [ 1 .. 1000000 ]
Default: 1
page_size
integer (Page Size) [ 1 .. 200 ]
Default: 50

Responses

Response samples

Content type
application/json
{
  • "has_more": true,
  • "items": [
    ],
  • "page": 1,
  • "page_size": 1,
  • "total": 0
}

Export audit log to CSV (admin) — streaming, capped at 100k rows

Authorizations:
bearerAuth
query Parameters
Actor User Id (string) or Actor User Id (null) (Actor User Id)
Target Table (string) or Target Table (null) (Target Table)
Action (string) or Action (null) (Action)
From (string) or From (null) (From)
To (string) or To (null) (To)
Q (string) or Q (null) (Q)

Responses

Response samples

Content type
application/problem+json
{
  • "detail": "string",
  • "instance": "string",
  • "status": 0,
  • "title": "string",
  • "type": "about:blank"
}

Export audit log to CSV (admin) — streaming, capped at 100k rows

Authorizations:
bearerAuth
query Parameters
Actor User Id (string) or Actor User Id (null) (Actor User Id)
Target Table (string) or Target Table (null) (Target Table)
Action (string) or Action (null) (Action)
From (string) or From (null) (From)
To (string) or To (null) (To)
Q (string) or Q (null) (Q)

Responses

Response samples

Content type
application/problem+json
{
  • "detail": "string",
  • "instance": "string",
  • "status": 0,
  • "title": "string",
  • "type": "about:blank"
}

List all backups (admin) — auto + manual, newest first

Authorizations:
bearerAuth

Responses

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "total": 0
}

List all backups (admin) — auto + manual, newest first

Authorizations:
bearerAuth

Responses

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "total": 0
}

Trigger a manual backup (admin) — enqueues the Celery task

Authorizations:
bearerAuth

Responses

Response samples

Content type
application/json
{
  • "name": "string",
  • "task_id": "string"
}

Trigger a manual backup (admin) — enqueues the Celery task

Authorizations:
bearerAuth

Responses

Response samples

Content type
application/json
{
  • "name": "string",
  • "task_id": "string"
}

Restore from an uploaded backup archive (admin) — destructive

Authorizations:
bearerAuth
header Parameters
X-Confirm-Restore (string) or X-Confirm-Restore (null) (X-Confirm-Restore)
Content-Length (integer) or Content-Length (null) (Content-Length)
Request Body schema: multipart/form-data
required
archive
required
string <application/octet-stream> (Archive)

tar.gz produced by GET /download.

Responses

Response samples

Content type
application/json
{
  • "message": "string",
  • "task_id": "string"
}

Restore from an uploaded backup archive (admin) — destructive

Authorizations:
bearerAuth
header Parameters
X-Confirm-Restore (string) or X-Confirm-Restore (null) (X-Confirm-Restore)
Content-Length (integer) or Content-Length (null) (Content-Length)
Request Body schema: multipart/form-data
required
archive
required
string <application/octet-stream> (Archive)

tar.gz produced by GET /download.

Responses

Response samples

Content type
application/json
{
  • "message": "string",
  • "task_id": "string"
}

Delete a manual backup (admin) — auto backups are protected

Authorizations:
bearerAuth
path Parameters
name
required
string (Name)

Backup directory name.

Responses

Response samples

Content type
application/problem+json
{
  • "detail": "string",
  • "instance": "string",
  • "status": 0,
  • "title": "string",
  • "type": "about:blank"
}

Delete a manual backup (admin) — auto backups are protected

Authorizations:
bearerAuth
path Parameters
name
required
string (Name)

Backup directory name.

Responses

Response samples

Content type
application/problem+json
{
  • "detail": "string",
  • "instance": "string",
  • "status": 0,
  • "title": "string",
  • "type": "about:blank"
}

Download a backup archive (admin) — streams a tar.gz of the directory

Authorizations:
bearerAuth
path Parameters
name
required
string (Name)

Backup directory name.

Responses

Response samples

Content type
application/json
null

Download a backup archive (admin) — streams a tar.gz of the directory

Authorizations:
bearerAuth
path Parameters
name
required
string (Name)

Backup directory name.

Responses

Response samples

Content type
application/json
null

Disk usage telemetry (admin) — workspace / DT volume / Postgres / Redis

Authorizations:
bearerAuth

Responses

Response samples

Content type
application/json
{
  • "collected_at": "2019-08-24T14:15:22Z",
  • "items": [
    ]
}

Disk usage telemetry (admin) — workspace / DT volume / Postgres / Redis

Authorizations:
bearerAuth

Responses

Response samples

Content type
application/json
{
  • "collected_at": "2019-08-24T14:15:22Z",
  • "items": [
    ]
}

endoflife.date snapshot status (admin) — dataset age / counters / next beat

Authorizations:
bearerAuth

Responses

Response samples

Content type
application/json
{
  • "cleared": 0,
  • "duration_ms": 0,
  • "enabled": true,
  • "eol_flagged_total": 0,
  • "feed_host": "string",
  • "last_attempt_at": "2019-08-24T14:15:22Z",
  • "last_result": "synced",
  • "last_synced_at": "2019-08-24T14:15:22Z",
  • "next_refresh_at": "2019-08-24T14:15:22Z",
  • "product_count": 0,
  • "refresh_enabled": true,
  • "rule_count": 0,
  • "skipped_reason": "string",
  • "snapshot_date": "2019-08-24",
  • "snapshot_origin": "vendored",
  • "stamped": 0
}

endoflife.date snapshot status (admin) — dataset age / counters / next beat

Authorizations:
bearerAuth

Responses

Response samples

Content type
application/json
{
  • "cleared": 0,
  • "duration_ms": 0,
  • "enabled": true,
  • "eol_flagged_total": 0,
  • "feed_host": "string",
  • "last_attempt_at": "2019-08-24T14:15:22Z",
  • "last_result": "synced",
  • "last_synced_at": "2019-08-24T14:15:22Z",
  • "next_refresh_at": "2019-08-24T14:15:22Z",
  • "product_count": 0,
  • "refresh_enabled": true,
  • "rule_count": 0,
  • "skipped_reason": "string",
  • "snapshot_date": "2019-08-24",
  • "snapshot_origin": "vendored",
  • "stamped": 0
}

System health summary (admin) — postgres / redis / celery / DT / disk

Authorizations:
bearerAuth

Responses

Response samples

Content type
application/json
{
  • "components": [
    ],
  • "updated_at": "2019-08-24T14:15:22Z"
}

System health summary (admin) — postgres / redis / celery / DT / disk

Authorizations:
bearerAuth

Responses

Response samples

Content type
application/json
{
  • "components": [
    ],
  • "updated_at": "2019-08-24T14:15:22Z"
}

CISA KEV feed sync status (admin) — last sync / counters / next beat

Authorizations:
bearerAuth

Responses

Response samples

Content type
application/json
{
  • "delisted": 0,
  • "duration_ms": 0,
  • "enabled": true,
  • "feed_count": 0,
  • "feed_host": "string",
  • "kev_flagged_total": 0,
  • "last_attempt_at": "2019-08-24T14:15:22Z",
  • "last_result": "synced",
  • "last_synced_at": "2019-08-24T14:15:22Z",
  • "listed": 0,
  • "next_refresh_at": "2019-08-24T14:15:22Z",
  • "skipped_reason": "string"
}

CISA KEV feed sync status (admin) — last sync / counters / next beat

Authorizations:
bearerAuth

Responses

Response samples

Content type
application/json
{
  • "delisted": 0,
  • "duration_ms": 0,
  • "enabled": true,
  • "feed_count": 0,
  • "feed_host": "string",
  • "kev_flagged_total": 0,
  • "last_attempt_at": "2019-08-24T14:15:22Z",
  • "last_result": "synced",
  • "last_synced_at": "2019-08-24T14:15:22Z",
  • "listed": 0,
  • "next_refresh_at": "2019-08-24T14:15:22Z",
  • "skipped_reason": "string"
}

Malicious-package snapshot status (admin) — age / counters / next beat

Authorizations:
bearerAuth

Responses

Response samples

Content type
application/json
{
  • "ecosystems": [
    ],
  • "enabled": true,
  • "flagged_total": 0,
  • "last_attempt_at": "2019-08-24T14:15:22Z",
  • "last_result": "synced",
  • "last_synced_at": "2019-08-24T14:15:22Z",
  • "newly_flagged": 0,
  • "next_refresh_at": "2019-08-24T14:15:22Z",
  • "purl_count": 0,
  • "refresh_enabled": true,
  • "skipped_reason": "string",
  • "snapshot_date": "2019-08-24",
  • "snapshot_stale": false,
  • "stamped": 0
}

Malicious-package snapshot status (admin) — age / counters / next beat

Authorizations:
bearerAuth

Responses

Response samples

Content type
application/json
{
  • "ecosystems": [
    ],
  • "enabled": true,
  • "flagged_total": 0,
  • "last_attempt_at": "2019-08-24T14:15:22Z",
  • "last_result": "synced",
  • "last_synced_at": "2019-08-24T14:15:22Z",
  • "newly_flagged": 0,
  • "next_refresh_at": "2019-08-24T14:15:22Z",
  • "purl_count": 0,
  • "refresh_enabled": true,
  • "skipped_reason": "string",
  • "snapshot_date": "2019-08-24",
  • "snapshot_stale": false,
  • "stamped": 0
}

List organizations (admin), paginated

Authorizations:
bearerAuth
query Parameters
page
integer (Page) [ 1 .. 1000000 ]
Default: 1
page_size
integer (Page Size) [ 1 .. 200 ]
Default: 50

Responses

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "page": 0,
  • "page_size": 0,
  • "total": 0
}

List organizations (admin), paginated

Authorizations:
bearerAuth
query Parameters
page
integer (Page) [ 1 .. 1000000 ]
Default: 1
page_size
integer (Page Size) [ 1 .. 200 ]
Default: 50

Responses

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "page": 0,
  • "page_size": 0,
  • "total": 0
}

List an organization's registry logins (admin), never the passwords

Authorizations:
bearerAuth
path Parameters
organization_id
required
string <uuid> (Organization Id)

Responses

Response samples

Content type
application/json
{
  • "items": [
    ]
}

List an organization's registry logins (admin), never the passwords

Authorizations:
bearerAuth
path Parameters
organization_id
required
string <uuid> (Organization Id)

Responses

Response samples

Content type
application/json
{
  • "items": [
    ]
}

Create or replace an organization's login for one registry (admin)

Authorizations:
bearerAuth
path Parameters
organization_id
required
string <uuid> (Organization Id)
Request Body schema: application/json
required
password
required
string (Password) non-empty

Registry password or token. Stored as Fernet ciphertext and never returned by any route.

registry_host
required
string (Registry Host) [ 1 .. 255 ] characters

Registry host as it appears in an image reference: ghcr.io, registry.example.com, registry:5000. A pasted https://ghcr.io/ is normalised to ghcr.io, because the scan-time lookup uses the host parsed out of the image reference and would otherwise never match.

username
required
string (Username) [ 1 .. 255 ] characters

Responses

Request samples

Content type
application/json
{
  • "password": "string",
  • "registry_host": "string",
  • "username": "string"
}

Response samples

Content type
application/json
{
  • "allowed": true,
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "registry_host": "string",
  • "username": "string"
}

Create or replace an organization's login for one registry (admin)

Authorizations:
bearerAuth
path Parameters
organization_id
required
string <uuid> (Organization Id)
Request Body schema: application/json
required
password
required
string (Password) non-empty

Registry password or token. Stored as Fernet ciphertext and never returned by any route.

registry_host
required
string (Registry Host) [ 1 .. 255 ] characters

Registry host as it appears in an image reference: ghcr.io, registry.example.com, registry:5000. A pasted https://ghcr.io/ is normalised to ghcr.io, because the scan-time lookup uses the host parsed out of the image reference and would otherwise never match.

username
required
string (Username) [ 1 .. 255 ] characters

Responses

Request samples

Content type
application/json
{
  • "password": "string",
  • "registry_host": "string",
  • "username": "string"
}

Response samples

Content type
application/json
{
  • "allowed": true,
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "registry_host": "string",
  • "username": "string"
}

Remove an organization's login for one registry (admin)

Authorizations:
bearerAuth
path Parameters
organization_id
required
string <uuid> (Organization Id)
credential_id
required
string <uuid> (Credential Id)

Responses

Response samples

Content type
application/problem+json
{
  • "detail": "string",
  • "instance": "string",
  • "status": 0,
  • "title": "string",
  • "type": "about:blank"
}

Remove an organization's login for one registry (admin)

Authorizations:
bearerAuth
path Parameters
organization_id
required
string <uuid> (Organization Id)
credential_id
required
string <uuid> (Credential Id)

Responses

Response samples

Content type
application/problem+json
{
  • "detail": "string",
  • "instance": "string",
  • "status": 0,
  • "title": "string",
  • "type": "about:blank"
}

List an organization's ticket-tracker logins (admin), never the tokens

Authorizations:
bearerAuth
path Parameters
organization_id
required
string <uuid> (Organization Id)

Responses

Response samples

Content type
application/json
{
  • "items": [
    ]
}

List an organization's ticket-tracker logins (admin), never the tokens

Authorizations:
bearerAuth
path Parameters
organization_id
required
string <uuid> (Organization Id)

Responses

Response samples

Content type
application/json
{
  • "items": [
    ]
}

Create or replace an organization's login for one ticket tracker (admin)

Authorizations:
bearerAuth
path Parameters
organization_id
required
string <uuid> (Organization Id)
Request Body schema: application/json
required
api_token
required
string (Api Token) non-empty

Tracker API token or password. Stored as Fernet ciphertext and never returned by any route.

auth_scheme
string (Auth Scheme)
Default: "jira_basic"

One of ['jira_basic'].

host
required
string (Host) [ 1 .. 255 ] characters

Tracker host as it appears in a finding's ticket_url: mycompany.atlassian.net. A pasted https://mycompany.atlassian.net/ is normalised to the bare host, because the read-time lookup uses the host parsed out of that URL and would otherwise never match.

Username (string) or Username (null) (Username)

The tracker account's login (Jira Cloud: its email). Required for jira_basic.

Responses

Request samples

Content type
application/json
{
  • "api_token": "string",
  • "auth_scheme": "jira_basic",
  • "host": "string",
  • "username": "string"
}

Response samples

Content type
application/json
{
  • "auth_scheme": "string",
  • "host": "string",
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "username": "string"
}

Create or replace an organization's login for one ticket tracker (admin)

Authorizations:
bearerAuth
path Parameters
organization_id
required
string <uuid> (Organization Id)
Request Body schema: application/json
required
api_token
required
string (Api Token) non-empty

Tracker API token or password. Stored as Fernet ciphertext and never returned by any route.

auth_scheme
string (Auth Scheme)
Default: "jira_basic"

One of ['jira_basic'].

host
required
string (Host) [ 1 .. 255 ] characters

Tracker host as it appears in a finding's ticket_url: mycompany.atlassian.net. A pasted https://mycompany.atlassian.net/ is normalised to the bare host, because the read-time lookup uses the host parsed out of that URL and would otherwise never match.

Username (string) or Username (null) (Username)

The tracker account's login (Jira Cloud: its email). Required for jira_basic.

Responses

Request samples

Content type
application/json
{
  • "api_token": "string",
  • "auth_scheme": "jira_basic",
  • "host": "string",
  • "username": "string"
}

Response samples

Content type
application/json
{
  • "auth_scheme": "string",
  • "host": "string",
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "username": "string"
}

Remove an organization's login for one ticket tracker (admin)

Authorizations:
bearerAuth
path Parameters
organization_id
required
string <uuid> (Organization Id)
credential_id
required
string <uuid> (Credential Id)

Responses

Response samples

Content type
application/problem+json
{
  • "detail": "string",
  • "instance": "string",
  • "status": 0,
  • "title": "string",
  • "type": "about:blank"
}

Remove an organization's login for one ticket tracker (admin)

Authorizations:
bearerAuth
path Parameters
organization_id
required
string <uuid> (Organization Id)
credential_id
required
string <uuid> (Credential Id)

Responses

Response samples

Content type
application/problem+json
{
  • "detail": "string",
  • "instance": "string",
  • "status": 0,
  • "title": "string",
  • "type": "about:blank"
}

List scans (admin) — cross-team queue with optional status/kind/project filters

Authorizations:
bearerAuth
query Parameters
page
integer (Page) [ 1 .. 1000000 ]
Default: 1
page_size
integer (Page Size) [ 1 .. 200 ]
Default: 50
Status (string) or Status (null) (Status)
Kind (string) or Kind (null) (Kind)
Project (string) or Project (null) (Project)

Responses

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "page": 1,
  • "page_size": 1,
  • "total": 0
}

List scans (admin) — cross-team queue with optional status/kind/project filters

Authorizations:
bearerAuth
query Parameters
page
integer (Page) [ 1 .. 1000000 ]
Default: 1
page_size
integer (Page Size) [ 1 .. 200 ]
Default: 50
Status (string) or Status (null) (Status)
Kind (string) or Kind (null) (Kind)
Project (string) or Project (null) (Project)

Responses

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "page": 1,
  • "page_size": 1,
  • "total": 0
}

Force-cancel a scan (admin) — Celery revoke + status='cancelled'

Authorizations:
bearerAuth
path Parameters
scan_id
required
string <uuid> (Scan Id)

Responses

Response samples

Content type
application/json
null

Force-cancel a scan (admin) — Celery revoke + status='cancelled'

Authorizations:
bearerAuth
path Parameters
scan_id
required
string <uuid> (Scan Id)

Responses

Response samples

Content type
application/json
null

List teams (admin) — paginated, name search

Authorizations:
bearerAuth
query Parameters
page
integer (Page) [ 1 .. 1000000 ]
Default: 1
page_size
integer (Page Size) [ 1 .. 200 ]
Default: 50
Search (string) or Search (null) (Search)

Responses

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "page": 0,
  • "page_size": 0,
  • "total": 0
}

List teams (admin) — paginated, name search

Authorizations:
bearerAuth
query Parameters
page
integer (Page) [ 1 .. 1000000 ]
Default: 1
page_size
integer (Page Size) [ 1 .. 200 ]
Default: 50
Search (string) or Search (null) (Search)

Responses

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "page": 0,
  • "page_size": 0,
  • "total": 0
}

Create a team (admin)

Authorizations:
bearerAuth
Request Body schema: application/json
required
Description (string) or Description (null) (Description)
name
required
string (Name) [ 1 .. 255 ] characters
Organization Id (string) or Organization Id (null) (Organization Id)

Which Organization the team belongs to. Required once a deployment has more than one (self-signup creates a personal Organization per user, so a demo SaaS deployment usually does): the create call refuses with 422 rather than silently guessing. Omit on a single-organization deployment; see GET /v1/admin/organizations to find the id.

slug
required
string (Slug) [ 1 .. 64 ] characters

Responses

Request samples

Content type
application/json
{
  • "description": "string",
  • "name": "string",
  • "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6",
  • "slug": "string"
}

Response samples

Content type
application/json
{
  • "created_at": "2019-08-24T14:15:22Z",
  • "description": "string",
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "members": [
    ],
  • "name": "string",
  • "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6",
  • "parent_group_id": "fe5e9949-7da4-4318-86fb-b3b64b2df380",
  • "project_count": 0,
  • "slug": "string",
  • "updated_at": "2019-08-24T14:15:22Z"
}

Create a team (admin)

Authorizations:
bearerAuth
Request Body schema: application/json
required
Description (string) or Description (null) (Description)
name
required
string (Name) [ 1 .. 255 ] characters
Organization Id (string) or Organization Id (null) (Organization Id)

Which Organization the team belongs to. Required once a deployment has more than one (self-signup creates a personal Organization per user, so a demo SaaS deployment usually does): the create call refuses with 422 rather than silently guessing. Omit on a single-organization deployment; see GET /v1/admin/organizations to find the id.

slug
required
string (Slug) [ 1 .. 64 ] characters

Responses

Request samples

Content type
application/json
{
  • "description": "string",
  • "name": "string",
  • "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6",
  • "slug": "string"
}

Response samples

Content type
application/json
{
  • "created_at": "2019-08-24T14:15:22Z",
  • "description": "string",
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "members": [
    ],
  • "name": "string",
  • "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6",
  • "parent_group_id": "fe5e9949-7da4-4318-86fb-b3b64b2df380",
  • "project_count": 0,
  • "slug": "string",
  • "updated_at": "2019-08-24T14:15:22Z"
}

Move a group (and its subtree) under a new parent, or to root (admin)

Authorizations:
bearerAuth
path Parameters
group_id
required
string <uuid> (Group Id)
Request Body schema: application/json
required
New Parent Id (string) or New Parent Id (null) (New Parent Id)

The group to move this group (and its whole subtree) under. Null moves the group to the root of its own organization.

Any of
string <uuid> (New Parent Id)

The group to move this group (and its whole subtree) under. Null moves the group to the root of its own organization.

Responses

Request samples

Content type
application/json
{
  • "new_parent_id": "95240cae-bfce-4d31-9b0f-a42763cd03ac"
}

Response samples

Content type
application/json
{
  • "created_at": "2019-08-24T14:15:22Z",
  • "description": "string",
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "members": [
    ],
  • "name": "string",
  • "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6",
  • "parent_group_id": "fe5e9949-7da4-4318-86fb-b3b64b2df380",
  • "project_count": 0,
  • "slug": "string",
  • "updated_at": "2019-08-24T14:15:22Z"
}

Move a group (and its subtree) under a new parent, or to root (admin)

Authorizations:
bearerAuth
path Parameters
group_id
required
string <uuid> (Group Id)
Request Body schema: application/json
required
New Parent Id (string) or New Parent Id (null) (New Parent Id)

The group to move this group (and its whole subtree) under. Null moves the group to the root of its own organization.

Any of
string <uuid> (New Parent Id)

The group to move this group (and its whole subtree) under. Null moves the group to the root of its own organization.

Responses

Request samples

Content type
application/json
{
  • "new_parent_id": "95240cae-bfce-4d31-9b0f-a42763cd03ac"
}

Response samples

Content type
application/json
{
  • "created_at": "2019-08-24T14:15:22Z",
  • "description": "string",
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "members": [
    ],
  • "name": "string",
  • "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6",
  • "parent_group_id": "fe5e9949-7da4-4318-86fb-b3b64b2df380",
  • "project_count": 0,
  • "slug": "string",
  • "updated_at": "2019-08-24T14:15:22Z"
}

Create a group nested directly under parent_group_id (admin)

Authorizations:
bearerAuth
path Parameters
parent_group_id
required
string <uuid> (Parent Group Id)
Request Body schema: application/json
required
Description (string) or Description (null) (Description)
name
required
string (Name) [ 1 .. 255 ] characters
slug
required
string (Slug) [ 1 .. 64 ] characters

Responses

Request samples

Content type
application/json
{
  • "description": "string",
  • "name": "string",
  • "slug": "string"
}

Response samples

Content type
application/json
{
  • "created_at": "2019-08-24T14:15:22Z",
  • "description": "string",
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "members": [
    ],
  • "name": "string",
  • "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6",
  • "parent_group_id": "fe5e9949-7da4-4318-86fb-b3b64b2df380",
  • "project_count": 0,
  • "slug": "string",
  • "updated_at": "2019-08-24T14:15:22Z"
}

Create a group nested directly under parent_group_id (admin)

Authorizations:
bearerAuth
path Parameters
parent_group_id
required
string <uuid> (Parent Group Id)
Request Body schema: application/json
required
Description (string) or Description (null) (Description)
name
required
string (Name) [ 1 .. 255 ] characters
slug
required
string (Slug) [ 1 .. 64 ] characters

Responses

Request samples

Content type
application/json
{
  • "description": "string",
  • "name": "string",
  • "slug": "string"
}

Response samples

Content type
application/json
{
  • "created_at": "2019-08-24T14:15:22Z",
  • "description": "string",
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "members": [
    ],
  • "name": "string",
  • "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6",
  • "parent_group_id": "fe5e9949-7da4-4318-86fb-b3b64b2df380",
  • "project_count": 0,
  • "slug": "string",
  • "updated_at": "2019-08-24T14:15:22Z"
}

Delete a team (admin) — archives projects, refuses on active scans

Authorizations:
bearerAuth
path Parameters
team_id
required
string <uuid> (Team Id)

Responses

Response samples

Content type
application/problem+json
{
  • "detail": "string",
  • "instance": "string",
  • "status": 0,
  • "title": "string",
  • "type": "about:blank"
}

Delete a team (admin) — archives projects, refuses on active scans

Authorizations:
bearerAuth
path Parameters
team_id
required
string <uuid> (Team Id)

Responses

Response samples

Content type
application/problem+json
{
  • "detail": "string",
  • "instance": "string",
  • "status": 0,
  • "title": "string",
  • "type": "about:blank"
}

Get one team (admin) — detail with members and project count

Authorizations:
bearerAuth
path Parameters
team_id
required
string <uuid> (Team Id)

Responses

Response samples

Content type
application/json
{
  • "created_at": "2019-08-24T14:15:22Z",
  • "description": "string",
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "members": [
    ],
  • "name": "string",
  • "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6",
  • "parent_group_id": "fe5e9949-7da4-4318-86fb-b3b64b2df380",
  • "project_count": 0,
  • "slug": "string",
  • "updated_at": "2019-08-24T14:15:22Z"
}

Get one team (admin) — detail with members and project count

Authorizations:
bearerAuth
path Parameters
team_id
required
string <uuid> (Team Id)

Responses

Response samples

Content type
application/json
{
  • "created_at": "2019-08-24T14:15:22Z",
  • "description": "string",
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "members": [
    ],
  • "name": "string",
  • "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6",
  • "parent_group_id": "fe5e9949-7da4-4318-86fb-b3b64b2df380",
  • "project_count": 0,
  • "slug": "string",
  • "updated_at": "2019-08-24T14:15:22Z"
}

Update a team (admin)

Authorizations:
bearerAuth
path Parameters
team_id
required
string <uuid> (Team Id)
Request Body schema: application/json
required
Description (string) or Description (null) (Description)
Name (string) or Name (null) (Name)
Slug (string) or Slug (null) (Slug)

Responses

Request samples

Content type
application/json
{
  • "description": "string",
  • "name": "string",
  • "slug": "string"
}

Response samples

Content type
application/json
{
  • "created_at": "2019-08-24T14:15:22Z",
  • "description": "string",
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "members": [
    ],
  • "name": "string",
  • "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6",
  • "parent_group_id": "fe5e9949-7da4-4318-86fb-b3b64b2df380",
  • "project_count": 0,
  • "slug": "string",
  • "updated_at": "2019-08-24T14:15:22Z"
}

Update a team (admin)

Authorizations:
bearerAuth
path Parameters
team_id
required
string <uuid> (Team Id)
Request Body schema: application/json
required
Description (string) or Description (null) (Description)
Name (string) or Name (null) (Name)
Slug (string) or Slug (null) (Slug)

Responses

Request samples

Content type
application/json
{
  • "description": "string",
  • "name": "string",
  • "slug": "string"
}

Response samples

Content type
application/json
{
  • "created_at": "2019-08-24T14:15:22Z",
  • "description": "string",
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "members": [
    ],
  • "name": "string",
  • "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6",
  • "parent_group_id": "fe5e9949-7da4-4318-86fb-b3b64b2df380",
  • "project_count": 0,
  • "slug": "string",
  • "updated_at": "2019-08-24T14:15:22Z"
}

Add (or update) a team member (admin)

Authorizations:
bearerAuth
path Parameters
team_id
required
string <uuid> (Team Id)
Request Body schema: application/json
required
role
required
string (Role)

One of team_admin, developer or viewer.

user_id
required
string <uuid> (User Id)

Responses

Request samples

Content type
application/json
{
  • "role": "string",
  • "user_id": "a169451c-8525-4352-b8ca-070dd449a1a5"
}

Response samples

Content type
application/json
{
  • "created_at": "2019-08-24T14:15:22Z",
  • "description": "string",
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "members": [
    ],
  • "name": "string",
  • "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6",
  • "parent_group_id": "fe5e9949-7da4-4318-86fb-b3b64b2df380",
  • "project_count": 0,
  • "slug": "string",
  • "updated_at": "2019-08-24T14:15:22Z"
}

Add (or update) a team member (admin)

Authorizations:
bearerAuth
path Parameters
team_id
required
string <uuid> (Team Id)
Request Body schema: application/json
required
role
required
string (Role)

One of team_admin, developer or viewer.

user_id
required
string <uuid> (User Id)

Responses

Request samples

Content type
application/json
{
  • "role": "string",
  • "user_id": "a169451c-8525-4352-b8ca-070dd449a1a5"
}

Response samples

Content type
application/json
{
  • "created_at": "2019-08-24T14:15:22Z",
  • "description": "string",
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "members": [
    ],
  • "name": "string",
  • "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6",
  • "parent_group_id": "fe5e9949-7da4-4318-86fb-b3b64b2df380",
  • "project_count": 0,
  • "slug": "string",
  • "updated_at": "2019-08-24T14:15:22Z"
}

Remove a team member (admin)

Authorizations:
bearerAuth
path Parameters
team_id
required
string <uuid> (Team Id)
user_id
required
string <uuid> (User Id)

Responses

Response samples

Content type
application/json
{
  • "created_at": "2019-08-24T14:15:22Z",
  • "description": "string",
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "members": [
    ],
  • "name": "string",
  • "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6",
  • "parent_group_id": "fe5e9949-7da4-4318-86fb-b3b64b2df380",
  • "project_count": 0,
  • "slug": "string",
  • "updated_at": "2019-08-24T14:15:22Z"
}

Remove a team member (admin)

Authorizations:
bearerAuth
path Parameters
team_id
required
string <uuid> (Team Id)
user_id
required
string <uuid> (User Id)

Responses

Response samples

Content type
application/json
{
  • "created_at": "2019-08-24T14:15:22Z",
  • "description": "string",
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "members": [
    ],
  • "name": "string",
  • "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6",
  • "parent_group_id": "fe5e9949-7da4-4318-86fb-b3b64b2df380",
  • "project_count": 0,
  • "slug": "string",
  • "updated_at": "2019-08-24T14:15:22Z"
}

Trivy vulnerability DB status (admin) — last_update / freshness / vuln_count

Authorizations:
bearerAuth

Responses

Response samples

Content type
application/json
{
  • "cache_dir": "string",
  • "db_size_bytes": 0,
  • "db_version": "string",
  • "freshness": "fresh",
  • "last_update": "2019-08-24T14:15:22Z",
  • "next_refresh_at": "2019-08-24T14:15:22Z",
  • "refresh_interval_hours": 1,
  • "repository": "string",
  • "vuln_count": 0
}

Trivy vulnerability DB status (admin) — last_update / freshness / vuln_count

Authorizations:
bearerAuth

Responses

Response samples

Content type
application/json
{
  • "cache_dir": "string",
  • "db_size_bytes": 0,
  • "db_version": "string",
  • "freshness": "fresh",
  • "last_update": "2019-08-24T14:15:22Z",
  • "next_refresh_at": "2019-08-24T14:15:22Z",
  • "refresh_interval_hours": 1,
  • "repository": "string",
  • "vuln_count": 0
}

List users (admin) — paginated, filterable

Authorizations:
bearerAuth
query Parameters
page
integer (Page) [ 1 .. 1000000 ]
Default: 1
page_size
integer (Page Size) [ 1 .. 200 ]
Default: 50
Role (string) or Role (null) (Role)
Active (boolean) or Active (null) (Active)
Search (string) or Search (null) (Search)

Responses

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "page": 0,
  • "page_size": 0,
  • "total": 0
}

List users (admin) — paginated, filterable

Authorizations:
bearerAuth
query Parameters
page
integer (Page) [ 1 .. 1000000 ]
Default: 1
page_size
integer (Page Size) [ 1 .. 200 ]
Default: 50
Role (string) or Role (null) (Role)
Active (boolean) or Active (null) (Active)
Search (string) or Search (null) (Search)

Responses

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "page": 0,
  • "page_size": 0,
  • "total": 0
}

Add one person

Authorizations:
bearerAuth
Request Body schema: application/json
required
email
required
string <email> (Email)
Full Name (string) or Full Name (null) (Full Name)
Password (string) or Password (null) (Password)

Omit on a deployment where people sign in through an identity provider: the account is created with no password set, so it cannot be signed into until somebody sets one through the reset flow. A password given here is held to the same policy as one chosen at signup.

Role (string) or Role (null) (Role)

Their grade on that team. Omitted follows the deployment's DEFAULT_MEMBER_ROLE. super_admin is not assignable here.

Team Id (string) or Team Id (null) (Team Id)

The team to put them on. Omitted leaves them on no team, which is a real state: they can sign in and see nothing until somebody adds them.

Responses

Request samples

Content type
application/json
{
  • "email": "user@example.com",
  • "full_name": "string",
  • "password": "stringst",
  • "role": "group_admin",
  • "team_id": "810007d0-bec5-486c-b5d1-28fcd8a079ba"
}

Response samples

Content type
application/json
{
  • "created_at": "2019-08-24T14:15:22Z",
  • "email": "string",
  • "full_name": "string",
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "is_active": true,
  • "is_superuser": true,
  • "last_login_at": "2019-08-24T14:15:22Z",
  • "memberships": [
    ],
  • "mfa_enabled": false,
  • "scan_count": 0,
  • "updated_at": "2019-08-24T14:15:22Z"
}

Add one person

Authorizations:
bearerAuth
Request Body schema: application/json
required
email
required
string <email> (Email)
Full Name (string) or Full Name (null) (Full Name)
Password (string) or Password (null) (Password)

Omit on a deployment where people sign in through an identity provider: the account is created with no password set, so it cannot be signed into until somebody sets one through the reset flow. A password given here is held to the same policy as one chosen at signup.

Role (string) or Role (null) (Role)

Their grade on that team. Omitted follows the deployment's DEFAULT_MEMBER_ROLE. super_admin is not assignable here.

Team Id (string) or Team Id (null) (Team Id)

The team to put them on. Omitted leaves them on no team, which is a real state: they can sign in and see nothing until somebody adds them.

Responses

Request samples

Content type
application/json
{
  • "email": "user@example.com",
  • "full_name": "string",
  • "password": "stringst",
  • "role": "group_admin",
  • "team_id": "810007d0-bec5-486c-b5d1-28fcd8a079ba"
}

Response samples

Content type
application/json
{
  • "created_at": "2019-08-24T14:15:22Z",
  • "email": "string",
  • "full_name": "string",
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "is_active": true,
  • "is_superuser": true,
  • "last_login_at": "2019-08-24T14:15:22Z",
  • "memberships": [
    ],
  • "mfa_enabled": false,
  • "scan_count": 0,
  • "updated_at": "2019-08-24T14:15:22Z"
}

Add many people, reporting each row

Authorizations:
bearerAuth
Request Body schema: application/json
required
required
Array of objects (Users) [ 1 .. 500 ] items
Array ([ 1 .. 500 ] items)
email
required
string <email> (Email)
Full Name (string) or Full Name (null) (Full Name)
Password (string) or Password (null) (Password)

Omit on a deployment where people sign in through an identity provider: the account is created with no password set, so it cannot be signed into until somebody sets one through the reset flow. A password given here is held to the same policy as one chosen at signup.

Role (string) or Role (null) (Role)

Their grade on that team. Omitted follows the deployment's DEFAULT_MEMBER_ROLE. super_admin is not assignable here.

Team Id (string) or Team Id (null) (Team Id)

The team to put them on. Omitted leaves them on no team, which is a real state: they can sign in and see nothing until somebody adds them.

Responses

Request samples

Content type
application/json
{
  • "users": [
    ]
}

Response samples

Content type
application/json
{
  • "failed": 0,
  • "results": [
    ],
  • "succeeded": 0,
  • "total": 0
}

Add many people, reporting each row

Authorizations:
bearerAuth
Request Body schema: application/json
required
required
Array of objects (Users) [ 1 .. 500 ] items
Array ([ 1 .. 500 ] items)
email
required
string <email> (Email)
Full Name (string) or Full Name (null) (Full Name)
Password (string) or Password (null) (Password)

Omit on a deployment where people sign in through an identity provider: the account is created with no password set, so it cannot be signed into until somebody sets one through the reset flow. A password given here is held to the same policy as one chosen at signup.

Role (string) or Role (null) (Role)

Their grade on that team. Omitted follows the deployment's DEFAULT_MEMBER_ROLE. super_admin is not assignable here.

Team Id (string) or Team Id (null) (Team Id)

The team to put them on. Omitted leaves them on no team, which is a real state: they can sign in and see nothing until somebody adds them.

Responses

Request samples

Content type
application/json
{
  • "users": [
    ]
}

Response samples

Content type
application/json
{
  • "failed": 0,
  • "results": [
    ],
  • "succeeded": 0,
  • "total": 0
}

Deactivate many people, reporting each row

Authorizations:
bearerAuth
Request Body schema: application/json
required
user_ids
required
Array of strings <uuid> (User Ids) [ 1 .. 500 ] items [ items <uuid > ]

Responses

Request samples

Content type
application/json
{
  • "user_ids": [
    ]
}

Response samples

Content type
application/json
{
  • "failed": 0,
  • "results": [
    ],
  • "succeeded": 0,
  • "total": 0
}

Deactivate many people, reporting each row

Authorizations:
bearerAuth
Request Body schema: application/json
required
user_ids
required
Array of strings <uuid> (User Ids) [ 1 .. 500 ] items [ items <uuid > ]

Responses

Request samples

Content type
application/json
{
  • "user_ids": [
    ]
}

Response samples

Content type
application/json
{
  • "failed": 0,
  • "results": [
    ],
  • "succeeded": 0,
  • "total": 0
}

The roster as CSV, in the shape the bulk import accepts back

Authorizations:
bearerAuth

Responses

Response samples

Content type
application/problem+json
{
  • "detail": "string",
  • "instance": "string",
  • "status": 0,
  • "title": "string",
  • "type": "about:blank"
}

The roster as CSV, in the shape the bulk import accepts back

Authorizations:
bearerAuth

Responses

Response samples

Content type
application/problem+json
{
  • "detail": "string",
  • "instance": "string",
  • "status": 0,
  • "title": "string",
  • "type": "about:blank"
}

Get one user (admin) — detail with memberships + scan count

Authorizations:
bearerAuth
path Parameters
user_id
required
string <uuid> (User Id)

Responses

Response samples

Content type
application/json
{
  • "created_at": "2019-08-24T14:15:22Z",
  • "email": "string",
  • "full_name": "string",
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "is_active": true,
  • "is_superuser": true,
  • "last_login_at": "2019-08-24T14:15:22Z",
  • "memberships": [
    ],
  • "mfa_enabled": false,
  • "scan_count": 0,
  • "updated_at": "2019-08-24T14:15:22Z"
}

Get one user (admin) — detail with memberships + scan count

Authorizations:
bearerAuth
path Parameters
user_id
required
string <uuid> (User Id)

Responses

Response samples

Content type
application/json
{
  • "created_at": "2019-08-24T14:15:22Z",
  • "email": "string",
  • "full_name": "string",
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "is_active": true,
  • "is_superuser": true,
  • "last_login_at": "2019-08-24T14:15:22Z",
  • "memberships": [
    ],
  • "mfa_enabled": false,
  • "scan_count": 0,
  • "updated_at": "2019-08-24T14:15:22Z"
}

Re-activate a user (admin)

Authorizations:
bearerAuth
path Parameters
user_id
required
string <uuid> (User Id)

Responses

Response samples

Content type
application/json
{
  • "created_at": "2019-08-24T14:15:22Z",
  • "email": "string",
  • "full_name": "string",
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "is_active": true,
  • "is_superuser": true,
  • "last_login_at": "2019-08-24T14:15:22Z",
  • "memberships": [
    ],
  • "mfa_enabled": false,
  • "scan_count": 0,
  • "updated_at": "2019-08-24T14:15:22Z"
}

Re-activate a user (admin)

Authorizations:
bearerAuth
path Parameters
user_id
required
string <uuid> (User Id)

Responses

Response samples

Content type
application/json
{
  • "created_at": "2019-08-24T14:15:22Z",
  • "email": "string",
  • "full_name": "string",
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "is_active": true,
  • "is_superuser": true,
  • "last_login_at": "2019-08-24T14:15:22Z",
  • "memberships": [
    ],
  • "mfa_enabled": false,
  • "scan_count": 0,
  • "updated_at": "2019-08-24T14:15:22Z"
}

Clear a person's second factor

Undoes somebody's second-factor enrolment: the flag, the secret, the replay counter and every unused recovery code, with existing sessions ended.

This exists because losing an authenticator is the ordinary failure of a second factor, and the alternatives do not cover it. A password reset cannot: unlocking the factor by mailbox would reduce it to owning the mailbox, which is what the first factor already proves. Recovery codes do, for anybody who kept them.

-- What this does not protect against ------------------------------------

A compromised super admin. That is not new, and it is not created here: the same account can already change roles and create users, so it can mint itself a fresh super admin with no second factor. What this adds is the ability to impersonate one specific existing person, and the notification below is what narrows that: somebody who did not ask for their factor to be cleared finds out that it was.

Deliberately not two-person: an unlock is reversible, since the owner can enrol again, and making somebody who is locked out wait for a second approver costs more than it buys.

Authorizations:
bearerAuth
path Parameters
user_id
required
string <uuid> (User Id)

Responses

Response samples

Content type
application/problem+json
{
  • "detail": "string",
  • "instance": "string",
  • "status": 0,
  • "title": "string",
  • "type": "about:blank"
}

Clear a person's second factor

Undoes somebody's second-factor enrolment: the flag, the secret, the replay counter and every unused recovery code, with existing sessions ended.

This exists because losing an authenticator is the ordinary failure of a second factor, and the alternatives do not cover it. A password reset cannot: unlocking the factor by mailbox would reduce it to owning the mailbox, which is what the first factor already proves. Recovery codes do, for anybody who kept them.

-- What this does not protect against ------------------------------------

A compromised super admin. That is not new, and it is not created here: the same account can already change roles and create users, so it can mint itself a fresh super admin with no second factor. What this adds is the ability to impersonate one specific existing person, and the notification below is what narrows that: somebody who did not ask for their factor to be cleared finds out that it was.

Deliberately not two-person: an unlock is reversible, since the owner can enrol again, and making somebody who is locked out wait for a second approver costs more than it buys.

Authorizations:
bearerAuth
path Parameters
user_id
required
string <uuid> (User Id)

Responses

Response samples

Content type
application/problem+json
{
  • "detail": "string",
  • "instance": "string",
  • "status": 0,
  • "title": "string",
  • "type": "about:blank"
}

Deactivate a user (admin) — revokes refresh tokens

Authorizations:
bearerAuth
path Parameters
user_id
required
string <uuid> (User Id)

Responses

Response samples

Content type
application/json
{
  • "created_at": "2019-08-24T14:15:22Z",
  • "email": "string",
  • "full_name": "string",
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "is_active": true,
  • "is_superuser": true,
  • "last_login_at": "2019-08-24T14:15:22Z",
  • "memberships": [
    ],
  • "mfa_enabled": false,
  • "scan_count": 0,
  • "updated_at": "2019-08-24T14:15:22Z"
}

Deactivate a user (admin) — revokes refresh tokens

Authorizations:
bearerAuth
path Parameters
user_id
required
string <uuid> (User Id)

Responses

Response samples

Content type
application/json
{
  • "created_at": "2019-08-24T14:15:22Z",
  • "email": "string",
  • "full_name": "string",
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "is_active": true,
  • "is_superuser": true,
  • "last_login_at": "2019-08-24T14:15:22Z",
  • "memberships": [
    ],
  • "mfa_enabled": false,
  • "scan_count": 0,
  • "updated_at": "2019-08-24T14:15:22Z"
}

Initiate a password reset (admin) — email delivery is wired separately

Issues a one-shot reset token (bcrypt-hashed in storage) and returns 204.

A follow-up change wires the SMTP / Slack delivery channel. Until then the plaintext token is generated, persisted as a hash, audit-logged via the listener (which masks the hash to ***), and discarded.

-- Account-enumeration semantics ------------------------------------------

This endpoint returns 404 when user_id does not exist. That IS an enumeration oracle in isolation, but it is acceptable HERE because the route is super-admin-gated by require_super_admin_or_404 — any caller who can reach this code path is already authorised to read the full user list (GET /v1/admin/users), so the 404 leaks no information they did not already have. The trust boundary is ABOVE this endpoint, not at it.

The PUBLIC password-reset flow ("forgot password") MUST NOT copy this 404-on-miss pattern. That endpoint is unauthenticated, so a 404 vs. 204 split there would let an attacker enumerate registered emails (CWE-204 Observable Response Discrepancy). The public flow returns a uniform 204 regardless of whether the email exists, with the actual reset email sent only when a match is found.

Authorizations:
bearerAuth
path Parameters
user_id
required
string <uuid> (User Id)

Responses

Response samples

Content type
application/problem+json
{
  • "detail": "string",
  • "instance": "string",
  • "status": 0,
  • "title": "string",
  • "type": "about:blank"
}

Initiate a password reset (admin) — email delivery is wired separately

Issues a one-shot reset token (bcrypt-hashed in storage) and returns 204.

A follow-up change wires the SMTP / Slack delivery channel. Until then the plaintext token is generated, persisted as a hash, audit-logged via the listener (which masks the hash to ***), and discarded.

-- Account-enumeration semantics ------------------------------------------

This endpoint returns 404 when user_id does not exist. That IS an enumeration oracle in isolation, but it is acceptable HERE because the route is super-admin-gated by require_super_admin_or_404 — any caller who can reach this code path is already authorised to read the full user list (GET /v1/admin/users), so the 404 leaks no information they did not already have. The trust boundary is ABOVE this endpoint, not at it.

The PUBLIC password-reset flow ("forgot password") MUST NOT copy this 404-on-miss pattern. That endpoint is unauthenticated, so a 404 vs. 204 split there would let an attacker enumerate registered emails (CWE-204 Observable Response Discrepancy). The public flow returns a uniform 204 regardless of whether the email exists, with the actual reset email sent only when a match is found.

Authorizations:
bearerAuth
path Parameters
user_id
required
string <uuid> (User Id)

Responses

Response samples

Content type
application/problem+json
{
  • "detail": "string",
  • "instance": "string",
  • "status": 0,
  • "title": "string",
  • "type": "about:blank"
}

Change a user's role (admin)

Authorizations:
bearerAuth
path Parameters
user_id
required
string <uuid> (User Id)
Request Body schema: application/json
required
role
required
string (Role)

One of super_admin / group_admin / developer.

Team Id (string) or Team Id (null) (Team Id)

Required when role is group_admin or developer; ignored for super_admin.

Responses

Request samples

Content type
application/json
{
  • "role": "string",
  • "team_id": "810007d0-bec5-486c-b5d1-28fcd8a079ba"
}

Response samples

Content type
application/json
{
  • "created_at": "2019-08-24T14:15:22Z",
  • "email": "string",
  • "full_name": "string",
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "is_active": true,
  • "is_superuser": true,
  • "last_login_at": "2019-08-24T14:15:22Z",
  • "memberships": [
    ],
  • "mfa_enabled": false,
  • "scan_count": 0,
  • "updated_at": "2019-08-24T14:15:22Z"
}

Change a user's role (admin)

Authorizations:
bearerAuth
path Parameters
user_id
required
string <uuid> (User Id)
Request Body schema: application/json
required
role
required
string (Role)

One of super_admin / group_admin / developer.

Team Id (string) or Team Id (null) (Team Id)

Required when role is group_admin or developer; ignored for super_admin.

Responses

Request samples

Content type
application/json
{
  • "role": "string",
  • "team_id": "810007d0-bec5-486c-b5d1-28fcd8a079ba"
}

Response samples

Content type
application/json
{
  • "created_at": "2019-08-24T14:15:22Z",
  • "email": "string",
  • "full_name": "string",
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "is_active": true,
  • "is_superuser": true,
  • "last_login_at": "2019-08-24T14:15:22Z",
  • "memberships": [
    ],
  • "mfa_enabled": false,
  • "scan_count": 0,
  • "updated_at": "2019-08-24T14:15:22Z"
}

Clear a person's failed sign-in count

Clears the per-address failed sign-in count so the person can try again.

Failed sign-ins are counted per address, and a refusal lasts until its window runs out. That is a slowdown for somebody guessing and an inconvenience for somebody who mistyped, but it is also a way to keep an account's owner out on purpose: anybody who knows an email can supply failures for it. Two things answer that. Completing a password reset clears the count, which needs the inbox and so cannot be blocked by the person doing the guessing. This is the other, for somebody who has lost access to that inbox.

It is not otherwise recoverable by hand. The counter is keyed by an HMAC of the address, which is deliberate -- Redis then holds no list of who has tried to sign in -- and the cost is that an operator cannot find or delete one person's key without the deployment secret and a script.

Super-admin only, and audited: this acts on somebody else's account, and clearing a count during an attack is a decision somebody should be able to review afterwards.

Returns 404 for an unknown user, on the same reasoning as the sibling password-reset route: the caller can already list every user, so the 404 tells them nothing new. The public sign-in path must not copy it.

Authorizations:
bearerAuth
path Parameters
user_id
required
string <uuid> (User Id)

Responses

Response samples

Content type
application/problem+json
{
  • "detail": "string",
  • "instance": "string",
  • "status": 0,
  • "title": "string",
  • "type": "about:blank"
}

Clear a person's failed sign-in count

Clears the per-address failed sign-in count so the person can try again.

Failed sign-ins are counted per address, and a refusal lasts until its window runs out. That is a slowdown for somebody guessing and an inconvenience for somebody who mistyped, but it is also a way to keep an account's owner out on purpose: anybody who knows an email can supply failures for it. Two things answer that. Completing a password reset clears the count, which needs the inbox and so cannot be blocked by the person doing the guessing. This is the other, for somebody who has lost access to that inbox.

It is not otherwise recoverable by hand. The counter is keyed by an HMAC of the address, which is deliberate -- Redis then holds no list of who has tried to sign in -- and the cost is that an operator cannot find or delete one person's key without the deployment secret and a script.

Super-admin only, and audited: this acts on somebody else's account, and clearing a count during an attack is a decision somebody should be able to review afterwards.

Returns 404 for an unknown user, on the same reasoning as the sibling password-reset route: the caller can already list every user, so the 404 tells them nothing new. The public sign-in path must not copy it.

Authorizations:
bearerAuth
path Parameters
user_id
required
string <uuid> (User Id)

Responses

Response samples

Content type
application/problem+json
{
  • "detail": "string",
  • "instance": "string",
  • "status": 0,
  • "title": "string",
  • "type": "about:blank"
}

api-keys

Paginated list of API keys visible to the caller

Authorizations:
bearerAuth
query Parameters
Scope (string) or Scope (null) (Scope)
Team Id (string) or Team Id (null) (Team Id)
Project Id (string) or Project Id (null) (Project Id)
include_revoked
boolean (Include Revoked)
Default: false
page
integer (Page) [ 1 .. 1000000 ]
Default: 1
page_size
integer (Page Size) [ 1 .. 200 ]
Default: 50

Responses

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "page": 0,
  • "page_size": 0,
  • "total": 0
}

Issue a new API key (plaintext returned ONCE in raw_key)

Authorizations:
bearerAuth
Request Body schema: application/json
required
Expires In Days (integer) or Expires In Days (null) (Expires In Days)

Optional TTL in days. The key stops authenticating after this many days. Omit for a non-expiring key (CI keys should set one and rotate). Max 1825 (5 years).

name
required
string (Name) [ 1 .. 100 ] characters
permission_breadth
string (Permission Breadth)
Default: "read_only"
Enum: "read_write" "read_only"

What the key may do: 'read_only' (the default) or 'read_write'. A read-only key is refused every request that changes something, so a pipeline that just reads results cannot start a scan. Keys issued before this existed are read-write and stay that way.

Project Id (string) or Project Id (null) (Project Id)
scope
required
string (Scope)
Enum: "org" "team" "project"
Service Account Id (string) or Service Account Id (null) (Service Account Id)

Issue the key to an automation identity instead of to yourself. The key then lives as long as that identity does, rather than stopping when you are deactivated. Omit for a personal key, which keeps today's behaviour exactly.

Team Id (string) or Team Id (null) (Team Id)

Responses

Request samples

Content type
application/json
{
  • "expires_in_days": 1,
  • "name": "string",
  • "permission_breadth": "read_write",
  • "project_id": "405d8375-3514-403b-8c43-83ae74cfe0e9",
  • "scope": "org",
  • "service_account_id": "81f92c7b-60ae-411e-bc6f-e2dc93db8886",
  • "team_id": "810007d0-bec5-486c-b5d1-28fcd8a079ba"
}

Response samples

Content type
application/json
{
  • "created_at": "2019-08-24T14:15:22Z",
  • "created_by_user_id": "209f54c4-4c33-43bc-9c6a-ef4c65ad7473",
  • "expires_at": "2019-08-24T14:15:22Z",
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "key_prefix": "string",
  • "name": "string",
  • "permission_breadth": "read_write",
  • "project_id": "405d8375-3514-403b-8c43-83ae74cfe0e9",
  • "raw_key": "string",
  • "scope": "org",
  • "team_id": "810007d0-bec5-486c-b5d1-28fcd8a079ba"
}

Revoke (soft-delete) an API key

Authorizations:
bearerAuth
path Parameters
api_key_id
required
string <uuid> (Api Key Id)

Responses

Response samples

Content type
application/problem+json
{
  • "detail": "string",
  • "instance": "string",
  • "status": 0,
  • "title": "string",
  • "type": "about:blank"
}

Narrow a key to read-only (one-way)

Authorizations:
bearerAuth
path Parameters
api_key_id
required
string <uuid> (Api Key Id)
Request Body schema: application/json
required
permission_breadth
required
string (Permission Breadth)

The only accepted value. Breadth narrows and never widens: a key that has been sitting in a CI log should not be handed more privilege than it was issued with.

Value: "read_only"

Responses

Request samples

Content type
application/json
{
  • "permission_breadth": "read_only"
}

Response samples

Content type
application/json
{
  • "created_at": "2019-08-24T14:15:22Z",
  • "created_by_email": "string",
  • "created_by_user_id": "209f54c4-4c33-43bc-9c6a-ef4c65ad7473",
  • "expires_at": "2019-08-24T14:15:22Z",
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "key_prefix": "string",
  • "last_used_at": "2019-08-24T14:15:22Z",
  • "name": "string",
  • "permission_breadth": "read_write",
  • "project_id": "405d8375-3514-403b-8c43-83ae74cfe0e9",
  • "revoked_at": "2019-08-24T14:15:22Z",
  • "scope": "org",
  • "team_id": "810007d0-bec5-486c-b5d1-28fcd8a079ba"
}

approvals

Paginated list of approval requests (team-scoped; super_admin sees all)

Authorizations:
bearerAuth
query Parameters
Status (string) or Status (null) (Status)

Single status or a comma-separated list of statuses (e.g. pending,under_review).

Team Id (string) or Team Id (null) (Team Id)
Project Id (string) or Project Id (null) (Project Id)

Narrow the queue to one project. Applied after the team gate, so a project outside the caller's teams returns an empty page rather than an error, exactly as an unknown id does.

Requested By User Id (string) or Requested By User Id (null) (Requested By User Id)
From Dt (string) or From Dt (null) (From Dt)
To Dt (string) or To Dt (null) (To Dt)
page
integer (Page) [ 1 .. 1000000 ]
Default: 1
page_size
integer (Page Size) [ 1 .. 200 ]
Default: 50

Responses

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "page": 0,
  • "page_size": 0,
  • "total": 0
}

Open a new approval request for a component in a project

Authorizations:
bearerAuth
Request Body schema: application/json
required
component_id
required
string <uuid> (Component Id)
project_id
required
string <uuid> (Project Id)

Responses

Request samples

Content type
application/json
{
  • "component_id": "41afafe5-2a48-424a-baef-34c2ad44ef7b",
  • "project_id": "405d8375-3514-403b-8c43-83ae74cfe0e9"
}

Response samples

Content type
application/json
{
  • "component_id": "41afafe5-2a48-424a-baef-34c2ad44ef7b",
  • "component_name": "string",
  • "component_purl": "string",
  • "decided_at": "2019-08-24T14:15:22Z",
  • "decided_by_user_id": "c750fc7f-0b29-4c93-a0d7-9578b1a62969",
  • "decision_note": "string",
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "project_id": "405d8375-3514-403b-8c43-83ae74cfe0e9",
  • "project_name": "string",
  • "project_slug": "string",
  • "requested_at": "2019-08-24T14:15:22Z",
  • "requested_by_name": "string",
  • "requested_by_user_id": "ed1d5170-d6e9-4e60-8754-78ea9640e9db",
  • "status": "pending",
  • "team_id": "810007d0-bec5-486c-b5d1-28fcd8a079ba",
  • "version": 0
}

Delete a non-terminal approval (original requester, team_admin, or super_admin)

Authorizations:
bearerAuth
path Parameters
approval_id
required
string <uuid> (Approval Id)

Responses

Response samples

Content type
application/problem+json
{
  • "detail": "string",
  • "instance": "string",
  • "status": 0,
  • "title": "string",
  • "type": "about:blank"
}

Single approval detail — includes ETag header for optimistic concurrency

Authorizations:
bearerAuth
path Parameters
approval_id
required
string <uuid> (Approval Id)

Responses

Response samples

Content type
application/json
{
  • "component_id": "41afafe5-2a48-424a-baef-34c2ad44ef7b",
  • "component_name": "string",
  • "component_purl": "string",
  • "decided_at": "2019-08-24T14:15:22Z",
  • "decided_by_user_id": "c750fc7f-0b29-4c93-a0d7-9578b1a62969",
  • "decision_note": "string",
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "project_id": "405d8375-3514-403b-8c43-83ae74cfe0e9",
  • "project_name": "string",
  • "project_slug": "string",
  • "requested_at": "2019-08-24T14:15:22Z",
  • "requested_by_name": "string",
  • "requested_by_user_id": "ed1d5170-d6e9-4e60-8754-78ea9640e9db",
  • "status": "pending",
  • "team_id": "810007d0-bec5-486c-b5d1-28fcd8a079ba",
  • "version": 0
}

Transition an approval's status (requires If-Match header)

Authorizations:
bearerAuth
path Parameters
approval_id
required
string <uuid> (Approval Id)
header Parameters
If-Match (string) or If-Match (null) (If-Match)
Request Body schema: application/json
required
action
required
string (Action)
Enum: "under_review" "approved" "rejected"
Decision Note (string) or Decision Note (null) (Decision Note)

Responses

Request samples

Content type
application/json
{
  • "action": "under_review",
  • "decision_note": "string"
}

Response samples

Content type
application/json
{
  • "component_id": "41afafe5-2a48-424a-baef-34c2ad44ef7b",
  • "component_name": "string",
  • "component_purl": "string",
  • "decided_at": "2019-08-24T14:15:22Z",
  • "decided_by_user_id": "c750fc7f-0b29-4c93-a0d7-9578b1a62969",
  • "decision_note": "string",
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "project_id": "405d8375-3514-403b-8c43-83ae74cfe0e9",
  • "project_name": "string",
  • "project_slug": "string",
  • "requested_at": "2019-08-24T14:15:22Z",
  • "requested_by_name": "string",
  • "requested_by_user_id": "ed1d5170-d6e9-4e60-8754-78ea9640e9db",
  • "status": "pending",
  • "team_id": "810007d0-bec5-486c-b5d1-28fcd8a079ba",
  • "version": 0
}

audit

Search audit log scoped to the caller's teams (team_admin+)

Authorizations:
bearerAuth
query Parameters
Actor User Id (string) or Actor User Id (null) (Actor User Id)
Target Table (string) or Target Table (null) (Target Table)
Action (string) or Action (null) (Action)
From (string) or From (null) (From)
To (string) or To (null) (To)
Q (string) or Q (null) (Q)
page
integer (Page) [ 1 .. 1000000 ]
Default: 1
page_size
integer (Page Size) [ 1 .. 200 ]
Default: 50

Responses

Response samples

Content type
application/json
{
  • "has_more": true,
  • "items": [
    ],
  • "page": 1,
  • "page_size": 1,
  • "total": 0
}

client-errors

Report a frontend render crash

Authorizations:
bearerAuth
Request Body schema: application/json
required
Component Stack (string) or Component Stack (null) (Component Stack)

React's errorInfo.componentStack: which component tree threw.

message
required
string (Message) [ 1 .. 2000 ] characters

error.message.

Stack (string) or Stack (null) (Stack)

error.stack, if the browser set one.

url
required
string (Url) [ 1 .. 2000 ] characters

window.location.href at the moment of the crash.

Responses

Request samples

Content type
application/json
{
  • "component_stack": "string",
  • "message": "string",
  • "stack": "string",
  • "url": "string"
}

Response samples

Content type
application/problem+json
{
  • "detail": "string",
  • "instance": "string",
  • "status": 0,
  • "title": "string",
  • "type": "about:blank"
}

components

Component detail (drawer payload). 404 if component is invisible to caller.

Authorizations:
bearerAuth
path Parameters
component_id
required
string <uuid> (Component Id)

Responses

Response samples

Content type
application/json
{
  • "component_id": "41afafe5-2a48-424a-baef-34c2ad44ef7b",
  • "created_at": "2019-08-24T14:15:22Z",
  • "currency_latest": "string",
  • "currency_latest_release_date": "2019-08-24",
  • "currency_state": "current",
  • "dependency_scope": "required",
  • "depth": 0,
  • "direct": false,
  • "eol_cycle": "string",
  • "eol_date": "2019-08-24",
  • "eol_product": "string",
  • "eol_source": "string",
  • "eol_state": "eol",
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "license": "string",
  • "license_category": "forbidden",
  • "malicious_id": "string",
  • "malicious_source": "string",
  • "malicious_state": "flagged",
  • "name": "string",
  • "obligations": [
    ],
  • "project_id": "405d8375-3514-403b-8c43-83ae74cfe0e9",
  • "purl": "string",
  • "raw_data": { },
  • "severity_max": "critical",
  • "updated_at": "2019-08-24T14:15:22Z",
  • "version": "string",
  • "vulnerabilities": [
    ]
}

dashboard

Work waiting on a person, across the caller's projects (auth required)

Pending approvals, KEV findings against their CISA deadline, projects the build gate would block, and projects nothing has scanned recently.

Same scoping contract as /summary: the caller's identity is the scope, enforced in the service through the shared accessible-projects helper. The gate bucket aggregates the gate's inputs rather than calling evaluate_gate per project — see services.action_queue_service for why, and for the parity test that keeps the two in agreement.

Rate limited per actor. BUCKET_LIMIT caps the rows returned but not the work done: the aggregates scan every open finding across the caller's accessible projects, so cost grows with portfolio size even though query count does not. Without a limit, one token could hold the connection pool on a large deployment.

Authorizations:
bearerAuth

Responses

Response samples

Content type
application/json
{
  • "gate_blocked": [
    ],
  • "kev_sla": {
    },
  • "pending_approvals": 0,
  • "stale_projects": [
    ]
}

Teams and their projects, by risk (auth required)

Every project the caller can see, grouped by the team that owns it.

The grouping is the point: a list answers "which project is worst", this answers "which team is carrying the risk". Same scoping contract as the other three — the caller's identity is the scope, and team names are read only for teams that own a visible project, so a caller cannot enumerate the organisation's teams through an empty row.

Both the per-team and the overall project caps are display limits, and the response reports what they cut: a grid that silently showed a subset would invite the reader to conclude the rest is clean.

Authorizations:
bearerAuth

Responses

Response samples

Content type
application/json
{
  • "project_count": 0,
  • "shown_project_count": 0,
  • "shown_team_count": 0,
  • "team_count": 0,
  • "teams": [
    ],
  • "truncated": true
}

Portfolio overview for the caller's accessible projects (auth required)

Aggregate counts (projects, scans, severities, licenses, approvals) plus the 10 most recent scans, scoped to the caller's accessible projects.

Rate limited per actor on the same bucket and budget as the other three dashboard routes (/action-queue, /trends, /portfolio): this route used to be the one dashboard endpoint without a limiter, which made it the cheapest way to repeatedly re-run the portfolio-wide aggregate.

Authorizations:
bearerAuth

Responses

Response samples

Content type
application/json
{
  • "license_category_counts": {
    },
  • "pending_approvals_count": 2,
  • "project_count": 7,
  • "recent_scans": [
    ],
  • "scan_status_counts": {
    },
  • "vulnerability_severity_counts": {
    }
}

external-packages

Look up advisory metadata on deps.dev by CVE or GHSA id

Authorizations:
bearerAuth
path Parameters
advisory_id
required
string (Advisory Id) [ 1 .. 64 ] characters

Responses

Response samples

Content type
application/json
{
  • "advisory_id": "GHSA-f23m-r3pf-42rh",
  • "aliases": [
    ],
  • "cvss3_score": 6.5,
  • "cvss3_vector": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:L",
  • "found": true,
  • "title": "lodash vulnerable to Prototype Pollution"
}

Look up a package on deps.dev by exact ecosystem and name

Authorizations:
bearerAuth
query Parameters
ecosystem
required
string (Ecosystem) <= 64 characters

One of: cargo, go, maven, npm, nuget, pypi.

name
required
string (Name) [ 1 .. 255 ] characters

Responses

Response samples

Content type
application/json
{
  • "advisory_count": 0,
  • "advisory_ids": [ ],
  • "ecosystem": "npm",
  • "found": true,
  • "homepage_url": "https://lodash.com/",
  • "internal_projects": [ ],
  • "licenses": [
    ],
  • "name": "lodash",
  • "purl": "pkg:npm/lodash",
  • "source_repo_url": "git+https://github.com/lodash/lodash.git",
  • "version": "4.18.1"
}

gate-policies

What this project's build gate actually applies

Resolve the policy, then fill the gaps the way the gate itself does.

A value shown here without saying where it came from invites the wrong edit: an operator who sees a threshold and assumes their group set it will look for a row that does not exist. sources names the group(s) or organization that supplied each value, with deployment for the ones no policy decided. sources_legacy is the deprecated pre-group-hierarchy string form of the same information (see its own docstring).

This endpoint used to resolve and return a project's policy with no team/group membership check at all -- require_role("viewer") is a coarse, route-level floor, not a project-scoped one. Once sources started naming the actual contributing groups, that gap widened from leaking "a threshold is team-set" to leaking real group names and the ancestor chain to any authenticated stranger. Hidden rather than refused, same as get_team_policy's own scope check below: existence of another organization's project/group is not this caller's business either.

Authorizations:
bearerAuth
path Parameters
project_id
required
string <uuid> (Project Id)

Responses

Response samples

Content type
application/json
{
  • "approval_required_statuses": [
    ],
  • "epss_data_available": true,
  • "epss_last_synced_at": "2019-08-24T14:15:22Z",
  • "epss_refresh_enabled": false,
  • "epss_scored_cves": 0,
  • "epss_threshold": 0,
  • "malicious_blocks": true,
  • "project_id": "405d8375-3514-403b-8c43-83ae74cfe0e9",
  • "reachable_critical_only": true,
  • "sources": {
    },
  • "sources_legacy": {
    }
}

Whether this deployment has EPSS data behind its thresholds

Deployment-scoped, so the policy editor can qualify a threshold it shows.

Not derived from a project or a scan: the question an administrator has while setting a threshold is whether this deployment collects EPSS, and that has one answer regardless of which project they came from. The per-scan version of the question rides on the gate result instead.

No path parameter, so nothing to authorize beyond being a signed-in reader: the response describes the deployment's own configuration and carries no project, team or finding data.

Authorizations:
bearerAuth

Responses

Response samples

Content type
application/json
{
  • "available": true,
  • "last_synced_at": "2019-08-24T14:15:22Z",
  • "refresh_enabled": true,
  • "scored_cves": 0
}

Create or replace the organization default

Authorizations:
bearerAuth
path Parameters
organization_id
required
string <uuid> (Organization Id)
Request Body schema: application/json
required
Array of Approval Required Statuses (strings) or Approval Required Statuses (null) (Approval Required Statuses)

Finding statuses one person may not reach alone. Reaching one of these opens a request that somebody else decides. Null or empty means every transition stays a single action.

Epss Threshold (number) or Epss Threshold (null) (Epss Threshold)

Block when an open finding's exploit-prediction score reaches this. Null leaves the condition off, which is the behaviour with no policy.

Malicious Blocks (boolean) or Malicious Blocks (null) (Malicious Blocks)

Whether a package the malicious snapshot flags blocks the build. Unlike the other two this is on by default, so a policy row only ever turns it off deliberately.

Name (string) or Name (null) (Name)

Label for the policy. The scope is its identity; this is for the UI.

Reachable Critical Only (boolean) or Reachable Critical Only (null) (Reachable Critical Only)

Count only criticals an analyser proved reachable. This can only shrink the blocking set, never widen it, and it applies only to scans that were actually analysed.

Responses

Request samples

Content type
application/json
{
  • "approval_required_statuses": [
    ],
  • "epss_threshold": 1,
  • "malicious_blocks": true,
  • "name": "string",
  • "reachable_critical_only": true
}

Response samples

Content type
application/json
{
  • "approval_required_statuses": [
    ],
  • "created_at": "2019-08-24T14:15:22Z",
  • "epss_threshold": 0,
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "malicious_blocks": true,
  • "name": "string",
  • "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6",
  • "reachable_critical_only": true,
  • "team_id": "810007d0-bec5-486c-b5d1-28fcd8a079ba",
  • "updated_at": "2019-08-24T14:15:22Z"
}

Drop a team's policy so it follows its organization again

Authorizations:
bearerAuth
path Parameters
team_id
required
string <uuid> (Team Id)

Responses

Response samples

Content type
application/problem+json
{
  • "detail": "string",
  • "instance": "string",
  • "status": 0,
  • "title": "string",
  • "type": "about:blank"
}

Read a team's own policy row

Authorizations:
bearerAuth
path Parameters
team_id
required
string <uuid> (Team Id)

Responses

Response samples

Content type
application/json
{
  • "approval_required_statuses": [
    ],
  • "created_at": "2019-08-24T14:15:22Z",
  • "epss_threshold": 0,
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "malicious_blocks": true,
  • "name": "string",
  • "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6",
  • "reachable_critical_only": true,
  • "team_id": "810007d0-bec5-486c-b5d1-28fcd8a079ba",
  • "updated_at": "2019-08-24T14:15:22Z"
}

Create or replace a team's build-gate policy

Authorizations:
bearerAuth
path Parameters
team_id
required
string <uuid> (Team Id)
Request Body schema: application/json
required
Array of Approval Required Statuses (strings) or Approval Required Statuses (null) (Approval Required Statuses)

Finding statuses one person may not reach alone. Reaching one of these opens a request that somebody else decides. Null or empty means every transition stays a single action.

Epss Threshold (number) or Epss Threshold (null) (Epss Threshold)

Block when an open finding's exploit-prediction score reaches this. Null leaves the condition off, which is the behaviour with no policy.

Malicious Blocks (boolean) or Malicious Blocks (null) (Malicious Blocks)

Whether a package the malicious snapshot flags blocks the build. Unlike the other two this is on by default, so a policy row only ever turns it off deliberately.

Name (string) or Name (null) (Name)

Label for the policy. The scope is its identity; this is for the UI.

Reachable Critical Only (boolean) or Reachable Critical Only (null) (Reachable Critical Only)

Count only criticals an analyser proved reachable. This can only shrink the blocking set, never widen it, and it applies only to scans that were actually analysed.

Responses

Request samples

Content type
application/json
{
  • "approval_required_statuses": [
    ],
  • "epss_threshold": 1,
  • "malicious_blocks": true,
  • "name": "string",
  • "reachable_critical_only": true
}

Response samples

Content type
application/json
{
  • "approval_required_statuses": [
    ],
  • "created_at": "2019-08-24T14:15:22Z",
  • "epss_threshold": 0,
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "malicious_blocks": true,
  • "name": "string",
  • "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6",
  • "reachable_critical_only": true,
  • "team_id": "810007d0-bec5-486c-b5d1-28fcd8a079ba",
  • "updated_at": "2019-08-24T14:15:22Z"
}

github-app

Paginated list of GitHub App credentials visible to the caller

Authorizations:
bearerAuth
query Parameters
Team Id (string) or Team Id (null) (Team Id)
include_revoked
boolean (Include Revoked)
Default: false
page
integer (Page) [ 1 .. 1000000 ]
Default: 1
page_size
integer (Page Size) [ 1 .. 200 ]
Default: 50

Responses

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "page": 0,
  • "page_size": 0,
  • "total": 0
}

Register a GitHub App credential (private key encrypted at rest)

Authorizations:
bearerAuth
query Parameters
team_id
required
string <uuid> (Team Id)

The team that owns this credential.

Request Body schema: application/json
required
app_id
required
string (App Id) [ 1 .. 64 ] characters
App Slug (string) or App Slug (null) (App Slug)
private_key
required
string (Private Key) non-empty

The GitHub App PEM private key (plaintext). Accepted ONCE at registration, encrypted at rest, and never returned.

Webhook Secret (string) or Webhook Secret (null) (Webhook Secret)

Responses

Request samples

Content type
application/json
{
  • "app_id": "string",
  • "app_slug": "string",
  • "private_key": "string",
  • "webhook_secret": "string"
}

Response samples

Content type
application/json
{
  • "app_id": "string",
  • "app_slug": "string",
  • "created_at": "2019-08-24T14:15:22Z",
  • "created_by_user_id": "209f54c4-4c33-43bc-9c6a-ef4c65ad7473",
  • "has_private_key": true,
  • "has_webhook_secret": true,
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "revoked_at": "2019-08-24T14:15:22Z",
  • "team_id": "810007d0-bec5-486c-b5d1-28fcd8a079ba",
  • "updated_at": "2019-08-24T14:15:22Z"
}

Revoke (soft-delete) a GitHub App credential

Authorizations:
bearerAuth
path Parameters
credential_id
required
string <uuid> (Credential Id)

Responses

Response samples

Content type
application/problem+json
{
  • "detail": "string",
  • "instance": "string",
  • "status": 0,
  • "title": "string",
  • "type": "about:blank"
}

Fetch one GitHub App credential's metadata

Authorizations:
bearerAuth
path Parameters
credential_id
required
string <uuid> (Credential Id)

Responses

Response samples

Content type
application/json
{
  • "app_id": "string",
  • "app_slug": "string",
  • "created_at": "2019-08-24T14:15:22Z",
  • "created_by_user_id": "209f54c4-4c33-43bc-9c6a-ef4c65ad7473",
  • "has_private_key": true,
  • "has_webhook_secret": true,
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "revoked_at": "2019-08-24T14:15:22Z",
  • "team_id": "810007d0-bec5-486c-b5d1-28fcd8a079ba",
  • "updated_at": "2019-08-24T14:15:22Z"
}

List installations under a credential

Authorizations:
bearerAuth
path Parameters
credential_id
required
string <uuid> (Credential Id)
query Parameters
page
integer (Page) [ 1 .. 1000000 ]
Default: 1
page_size
integer (Page Size) [ 1 .. 200 ]
Default: 50

Responses

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "page": 0,
  • "page_size": 0,
  • "total": 0
}

Link (opt-in) an installation under a credential

Authorizations:
bearerAuth
path Parameters
credential_id
required
string <uuid> (Credential Id)
Request Body schema: application/json
required
Account Login (string) or Account Login (null) (Account Login)
installation_id
required
string (Installation Id) [ 1 .. 64 ] characters
Project Id (string) or Project Id (null) (Project Id)

The TrustedOSS project this installation is opted-in to.

Repository Full Name (string) or Repository Full Name (null) (Repository Full Name)

Responses

Request samples

Content type
application/json
{
  • "account_login": "string",
  • "installation_id": "string",
  • "project_id": "405d8375-3514-403b-8c43-83ae74cfe0e9",
  • "repository_full_name": "string"
}

Response samples

Content type
application/json
{
  • "account_login": "string",
  • "created_at": "2019-08-24T14:15:22Z",
  • "created_by_user_id": "209f54c4-4c33-43bc-9c6a-ef4c65ad7473",
  • "credential_id": "13c40afa-2996-4a0c-b2e9-68fba9462242",
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "installation_id": "string",
  • "project_id": "405d8375-3514-403b-8c43-83ae74cfe0e9",
  • "repository_full_name": "string",
  • "updated_at": "2019-08-24T14:15:22Z"
}

groups

List groups visible to the caller (flat search or one-level drill-down)

Authorizations:
bearerAuth
query Parameters
Q (string) or Q (null) (Q)

Flat, whole-tree name search. When set, parent_id is ignored, a match is returned regardless of which branch it lives under.

Parent Id (string) or Parent Id (null) (Parent Id)

Drill-down mode (only consulted when q is unset). Omitted returns root groups; set, returns that group's direct children. An inaccessible or nonexistent parent_id returns an empty page, not a 404, the list surface never confirms a hidden group's existence.

page
integer (Page) [ 1 .. 1000000 ]
Default: 1
page_size
integer (Page Size) [ 1 .. 200 ]
Default: 50

Responses

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "page": 0,
  • "page_size": 0,
  • "total": 0
}

Group detail: ancestors + 30-day subtree activity summary

Authorizations:
bearerAuth
path Parameters
group_id
required
string <uuid> (Group Id)

Responses

Response samples

Content type
application/json
{
  • "ancestors": [
    ],
  • "child_group_count": 0,
  • "created_at": "2019-08-24T14:15:22Z",
  • "description": "string",
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "member_count": 0,
  • "name": "string",
  • "parent_group_id": "fe5e9949-7da4-4318-86fb-b3b64b2df380",
  • "project_count": 0,
  • "slug": "string",
  • "stats": {
    },
  • "updated_at": "2019-08-24T14:15:22Z"
}

Group members: direct memberships vs. cascade-inherited ones

Authorizations:
bearerAuth
path Parameters
group_id
required
string <uuid> (Group Id)

Responses

Response samples

Content type
application/json
{
  • "direct": [
    ],
  • "inherited": [
    ]
}

intake-requests

Requests in the caller's teams, oldest first

Authorizations:
bearerAuth
query Parameters
Project Id (string) or Project Id (null) (Project Id)
Array of Status (strings) or Status (null) (Status)

Responses

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "total": 0
}

Ask to use a package

Authorizations:
bearerAuth
Request Body schema: application/json
required
justification
required
string (Justification) >= 10 characters

Why this package. The reviewer is being asked about something that is not in the codebase yet, so this is all they have.

project_id
required
string <uuid> (Project Id)
purl
required
string (Purl) [ 5 .. 512 ] characters

The package, as a purl: 'pkg:npm/lodash', 'pkg:pypi/requests'. A purl and not a name, because it is what a later scan will match the answer against.

Responses

Request samples

Content type
application/json
{
  • "justification": "stringstri",
  • "project_id": "405d8375-3514-403b-8c43-83ae74cfe0e9",
  • "purl": "string"
}

Response samples

Content type
application/json
{
  • "created_at": "2019-08-24T14:15:22Z",
  • "decided_at": "2019-08-24T14:15:22Z",
  • "decided_by_user_id": "c750fc7f-0b29-4c93-a0d7-9578b1a62969",
  • "decision_note": "string",
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "justification": "string",
  • "project_id": "405d8375-3514-403b-8c43-83ae74cfe0e9",
  • "purl": "string",
  • "requested_by_user_id": "ed1d5170-d6e9-4e60-8754-78ea9640e9db",
  • "status": "string",
  • "team_id": "810007d0-bec5-486c-b5d1-28fcd8a079ba",
  • "updated_at": "2019-08-24T14:15:22Z",
  • "version": 0
}

Answer a request, or move it along

Authorizations:
bearerAuth
path Parameters
request_id
required
string <uuid> (Request Id)
Request Body schema: application/json
required
Note (string) or Note (null) (Note)

Optional reasoning, kept either way.

status
required
string (Status)

under_review, approved or rejected.

Responses

Request samples

Content type
application/json
{
  • "note": "string",
  • "status": "string"
}

Response samples

Content type
application/json
{
  • "created_at": "2019-08-24T14:15:22Z",
  • "decided_at": "2019-08-24T14:15:22Z",
  • "decided_by_user_id": "c750fc7f-0b29-4c93-a0d7-9578b1a62969",
  • "decision_note": "string",
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "justification": "string",
  • "project_id": "405d8375-3514-403b-8c43-83ae74cfe0e9",
  • "purl": "string",
  • "requested_by_user_id": "ed1d5170-d6e9-4e60-8754-78ea9640e9db",
  • "status": "string",
  • "team_id": "810007d0-bec5-486c-b5d1-28fcd8a079ba",
  • "updated_at": "2019-08-24T14:15:22Z",
  • "version": 0
}

inventory

Organization-wide component inventory

Authorizations:
bearerAuth
query Parameters
limit
integer (Limit) [ 1 .. 200 ]
Default: 50
offset
integer (Offset) >= 0
Default: 0
Q (string) or Q (null) (Q)

Substring match on package name or purl. LIKE metacharacters are escaped and matched literally.

Array of Package Type (strings) or Package Type (null) (Package Type)
Array of Severity (strings) or Severity (null) (Severity)

Worst-severity buckets to keep. Unknown tokens are ignored.

Array of License Category (strings) or License Category (null) (License Category)
Eol (boolean) or Eol (null) (Eol)
Outdated (boolean) or Outdated (null) (Outdated)
sort
string (Sort) ^(name|project_count|severity|license)$
Default: "project_count"
order
string (Order) ^(asc|desc)$
Default: "desc"

Responses

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "limit": 0,
  • "offset": 0,
  • "total": 0
}

The filtered inventory as CSV

The same rows the list endpoint would return, without the paging.

The scope is the caller's own: the list service resolves which teams the actor can read and the export calls that same service, so a member of one team never receives another team's packages.

sort/order are still accepted and validated but no longer decide the exported rows' order (#463): the export walks a fixed key (the component id) instead, since this is the org-wide rollup, the export most likely to reach a depth where OFFSET's cost matters.

Authorizations:
bearerAuth
query Parameters
Q (string) or Q (null) (Q)
Array of Package Type (strings) or Package Type (null) (Package Type)
Array of Severity (strings) or Severity (null) (Severity)
Array of License Category (strings) or License Category (null) (License Category)
Eol (boolean) or Eol (null) (Eol)
Outdated (boolean) or Outdated (null) (Outdated)
sort
string (Sort) ^(name|project_count|severity|license)$
Default: "project_count"
order
string (Order) ^(asc|desc)$
Default: "desc"

Responses

Response samples

Content type
application/problem+json
{
  • "detail": "string",
  • "instance": "string",
  • "status": 0,
  • "title": "string",
  • "type": "about:blank"
}

Projects using a component

Authorizations:
bearerAuth
path Parameters
component_id
required
string <uuid> (Component Id)
query Parameters
limit
integer (Limit) [ 1 .. 200 ]
Default: 50
offset
integer (Offset) >= 0
Default: 0

Responses

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "limit": 0,
  • "offset": 0,
  • "total": 0
}

Projects affected by a CVE

Authorizations:
bearerAuth
path Parameters
external_id
required
string (External Id) <= 64 characters
query Parameters
limit
integer (Limit) [ 1 .. 200 ]
Default: 50
offset
integer (Offset) >= 0
Default: 0

Responses

Response samples

Content type
application/json
{
  • "external_id": "string",
  • "items": [
    ],
  • "limit": 0,
  • "offset": 0,
  • "severity": "critical",
  • "total": 0
}

license-policies

Paginated list of license policies visible to the caller

Authorizations:
bearerAuth
query Parameters
Organization Id (string) or Organization Id (null) (Organization Id)
Team Id (string) or Team Id (null) (Team Id)
page
integer (Page) [ 1 .. 1000000 ]
Default: 1
page_size
integer (Page Size) [ 1 .. 200 ]
Default: 50

Responses

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "page": 0,
  • "page_size": 0,
  • "total": 0
}

Read the org-default license policy (super_admin)

Authorizations:
bearerAuth
path Parameters
organization_id
required
string <uuid> (Organization Id)

Responses

Response samples

Content type
application/json
{
  • "category_overrides": {
    },
  • "compound_operator_strategy": {
    },
  • "created_at": "2019-08-24T14:15:22Z",
  • "created_by_user_id": "209f54c4-4c33-43bc-9c6a-ef4c65ad7473",
  • "enabled": true,
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "license_exceptions": [
    ],
  • "malicious_exceptions": [
    ],
  • "name": "string",
  • "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6",
  • "team_id": "810007d0-bec5-486c-b5d1-28fcd8a079ba",
  • "unknown_license_category": "string",
  • "updated_at": "2019-08-24T14:15:22Z"
}

Create or update the org-default license policy (super_admin)

Authorizations:
bearerAuth
path Parameters
organization_id
required
string <uuid> (Organization Id)
Request Body schema: application/json
required
object (Category Overrides)
object (Compound Operator Strategy)
enabled
boolean (Enabled)
Default: true
Array of objects (License Exceptions)
Array of objects (Malicious Exceptions)
Name (string) or Name (null) (Name)
unknown_license_category
string (Unknown License Category)
Default: "conditional"
Enum: "allowed" "conditional" "forbidden"

Responses

Request samples

Content type
application/json
{
  • "category_overrides": {
    },
  • "compound_operator_strategy": {
    },
  • "enabled": true,
  • "license_exceptions": [
    ],
  • "malicious_exceptions": [
    ],
  • "name": "string",
  • "unknown_license_category": "allowed"
}

Response samples

Content type
application/json
{
  • "category_overrides": {
    },
  • "compound_operator_strategy": {
    },
  • "created_at": "2019-08-24T14:15:22Z",
  • "created_by_user_id": "209f54c4-4c33-43bc-9c6a-ef4c65ad7473",
  • "enabled": true,
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "license_exceptions": [
    ],
  • "malicious_exceptions": [
    ],
  • "name": "string",
  • "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6",
  • "team_id": "810007d0-bec5-486c-b5d1-28fcd8a079ba",
  • "unknown_license_category": "string",
  • "updated_at": "2019-08-24T14:15:22Z"
}

Reset (delete) a team's license policy

Authorizations:
bearerAuth
path Parameters
team_id
required
string <uuid> (Team Id)

Responses

Response samples

Content type
application/problem+json
{
  • "detail": "string",
  • "instance": "string",
  • "status": 0,
  • "title": "string",
  • "type": "about:blank"
}

Read the effective license policy for a team

Authorizations:
bearerAuth
path Parameters
team_id
required
string <uuid> (Team Id)

Responses

Response samples

Content type
application/json
{
  • "category_overrides": {
    },
  • "compound_operator_strategy": {
    },
  • "created_at": "2019-08-24T14:15:22Z",
  • "created_by_user_id": "209f54c4-4c33-43bc-9c6a-ef4c65ad7473",
  • "enabled": true,
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "license_exceptions": [
    ],
  • "malicious_exceptions": [
    ],
  • "name": "string",
  • "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6",
  • "team_id": "810007d0-bec5-486c-b5d1-28fcd8a079ba",
  • "unknown_license_category": "string",
  • "updated_at": "2019-08-24T14:15:22Z"
}

Create or update a team's license policy

Authorizations:
bearerAuth
path Parameters
team_id
required
string <uuid> (Team Id)
Request Body schema: application/json
required
object (Category Overrides)
object (Compound Operator Strategy)
enabled
boolean (Enabled)
Default: true
Array of objects (License Exceptions)
Array of objects (Malicious Exceptions)
Name (string) or Name (null) (Name)
unknown_license_category
string (Unknown License Category)
Default: "conditional"
Enum: "allowed" "conditional" "forbidden"

Responses

Request samples

Content type
application/json
{
  • "category_overrides": {
    },
  • "compound_operator_strategy": {
    },
  • "enabled": true,
  • "license_exceptions": [
    ],
  • "malicious_exceptions": [
    ],
  • "name": "string",
  • "unknown_license_category": "allowed"
}

Response samples

Content type
application/json
{
  • "category_overrides": {
    },
  • "compound_operator_strategy": {
    },
  • "created_at": "2019-08-24T14:15:22Z",
  • "created_by_user_id": "209f54c4-4c33-43bc-9c6a-ef4c65ad7473",
  • "enabled": true,
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "license_exceptions": [
    ],
  • "malicious_exceptions": [
    ],
  • "name": "string",
  • "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6",
  • "team_id": "810007d0-bec5-486c-b5d1-28fcd8a079ba",
  • "unknown_license_category": "string",
  • "updated_at": "2019-08-24T14:15:22Z"
}

Un-waive (remove a license_exceptions entry from the team policy)

Authorizations:
bearerAuth
path Parameters
team_id
required
string <uuid> (Team Id)
query Parameters
spdx_id
required
string (Spdx Id) [ 1 .. 128 ] characters
Component Purl (string) or Component Purl (null) (Component Purl)

Responses

Response samples

Content type
application/json
{
  • "category_overrides": {
    },
  • "compound_operator_strategy": {
    },
  • "created_at": "2019-08-24T14:15:22Z",
  • "created_by_user_id": "209f54c4-4c33-43bc-9c6a-ef4c65ad7473",
  • "enabled": true,
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "license_exceptions": [
    ],
  • "malicious_exceptions": [
    ],
  • "name": "string",
  • "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6",
  • "team_id": "810007d0-bec5-486c-b5d1-28fcd8a079ba",
  • "unknown_license_category": "string",
  • "updated_at": "2019-08-24T14:15:22Z"
}

Waive a license (add a license_exceptions entry to the team policy)

Authorizations:
bearerAuth
path Parameters
team_id
required
string <uuid> (Team Id)
Request Body schema: application/json
required
Component Purl (string) or Component Purl (null) (Component Purl)
Expires At (string) or Expires At (null) (Expires At)
reason
required
string (Reason) [ 1 .. 1000 ] characters
spdx_id
required
string (Spdx Id) [ 1 .. 128 ] characters

Responses

Request samples

Content type
application/json
{
  • "component_purl": "string",
  • "expires_at": "2019-08-24T14:15:22Z",
  • "reason": "string",
  • "spdx_id": "string"
}

Response samples

Content type
application/json
{
  • "category_overrides": {
    },
  • "compound_operator_strategy": {
    },
  • "created_at": "2019-08-24T14:15:22Z",
  • "created_by_user_id": "209f54c4-4c33-43bc-9c6a-ef4c65ad7473",
  • "enabled": true,
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "license_exceptions": [
    ],
  • "malicious_exceptions": [
    ],
  • "name": "string",
  • "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6",
  • "team_id": "810007d0-bec5-486c-b5d1-28fcd8a079ba",
  • "unknown_license_category": "string",
  • "updated_at": "2019-08-24T14:15:22Z"
}

licenses

License finding drawer payload (404 if invisible to caller)

Authorizations:
bearerAuth
path Parameters
finding_id
required
string <uuid> (Finding Id)

Responses

Response samples

Content type
application/json
{
  • "affected_components": [
    ],
  • "affected_components_total": 0,
  • "affected_components_truncated": false,
  • "category": "allowed",
  • "created_at": "2019-08-24T14:15:22Z",
  • "finding_kind": "declared",
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "is_deprecated_license_id": false,
  • "is_fsf_libre": false,
  • "is_osi_approved": false,
  • "license_id": "38cf2dcf-77be-45c4-b03d-412af2743263",
  • "name": "string",
  • "ort_match": { },
  • "osori": {
    },
  • "reference_url": "string",
  • "review_flag": "behavioral_use",
  • "spdx_id": "string",
  • "summary": "string",
  • "summary_ko": "string",
  • "updated_at": "2019-08-24T14:15:22Z"
}

Per-license rows + category distribution for the project's latest scan

Authorizations:
bearerAuth
path Parameters
project_id
required
string <uuid> (Project Id)
query Parameters
limit
integer (Limit) [ 1 .. 500 ]
Default: 50
offset
integer (Offset) >= 0
Default: 0
Array of Category (strings) or Category (null) (Category)
Array of Kind (strings) or Kind (null) (Kind)
Search (string) or Search (null) (Search)
sort
string (Sort) ^(category|name|spdx_id|affected_count)$
Default: "category"
order
string (Order) ^(asc|desc)$
Default: "desc"
Review Flag (string) or Review Flag (null) (Review Flag)

Filter to licenses carrying an AI review flag (Phase D). behavioral_use = RAIL/Llama/Gemma/Falcon community licenses; non_commercial = CC-BY-NC…. Omit to list all licenses.

Conflict (string) or Conflict (null) (Conflict)

Filter to licenses carrying this outbound-conflict verdict (gap #27). Matches nothing when the project declares no outbound license, since nothing is assessed without one. Omit to list all.

Scan Id (string) or Scan Id (null) (Scan Id)

Optional release-snapshot anchor. Read this surface as of ONE specific succeeded scan instead of the project's current state. Must belong to this project and be succeeded, else 404. Takes precedence over release.

Release (string) or Release (null) (Release)

Optional version anchor — read this surface as of the release carrying this label (e.g. '4.0'). Equivalent to looking the label up on /releases?release= and pinning the scan_id it returns, but as one permanent URL. Matched exactly, whitespace trimmed. Unknown label → 404, and so does a label in a project you cannot read. Ignored when scan_id is also given.

Responses

Response samples

Content type
application/json
{
  • "conflict_summary": {
    },
  • "declared_license": "string",
  • "distribution": {
    },
  • "items": [
    ],
  • "total": 0
}

The filtered license list as CSV

The same rows the list endpoint would return, without the paging.

Filters are applied by the list service itself rather than by a second query, so the file and the screen cannot disagree, and the cross-team check the list performs is the one this performs.

Authorizations:
bearerAuth
path Parameters
project_id
required
string <uuid> (Project Id)
query Parameters
Array of Category (strings) or Category (null) (Category)
Array of Kind (strings) or Kind (null) (Kind)
Search (string) or Search (null) (Search)
sort
string (Sort) ^(category|name|spdx_id|affected_count)$
Default: "category"
order
string (Order) ^(asc|desc)$
Default: "desc"
Review Flag (string) or Review Flag (null) (Review Flag)
Conflict (string) or Conflict (null) (Conflict)
Scan Id (string) or Scan Id (null) (Scan Id)

Optional release-snapshot anchor. Read this surface as of ONE specific succeeded scan instead of the project's current state. Must belong to this project and be succeeded, else 404. Takes precedence over release.

Release (string) or Release (null) (Release)

Optional version anchor — read this surface as of the release carrying this label (e.g. '4.0'). Equivalent to looking the label up on /releases?release= and pinning the scan_id it returns, but as one permanent URL. Matched exactly, whitespace trimmed. Unknown label → 404, and so does a label in a project you cannot read. Ignored when scan_id is also given.

Responses

Response samples

Content type
application/problem+json
{
  • "detail": "string",
  • "instance": "string",
  • "status": 0,
  • "title": "string",
  • "type": "about:blank"
}

notice-templates

Remove the organization's NOTICE template for one format

Authorizations:
bearerAuth
path Parameters
organization_id
required
string <uuid> (Organization Id)
format
required
string (Format) ^(text|markdown|html)$

Responses

Response samples

Content type
application/problem+json
{
  • "detail": "string",
  • "instance": "string",
  • "status": 0,
  • "title": "string",
  • "type": "about:blank"
}

Read the organization's NOTICE template for one format

Authorizations:
bearerAuth
path Parameters
organization_id
required
string <uuid> (Organization Id)
format
required
string (Format) ^(text|markdown|html)$

Responses

Response samples

Content type
application/json
{
  • "created_at": "2019-08-24T14:15:22Z",
  • "footer": "string",
  • "format": "string",
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6",
  • "preface": "string",
  • "updated_at": "2019-08-24T14:15:22Z"
}

Create or replace the organization's NOTICE template for one format

Authorizations:
bearerAuth
path Parameters
organization_id
required
string <uuid> (Organization Id)
format
required
string (Format) ^(text|markdown|html)$
Request Body schema: application/json
required
Footer (string) or Footer (null) (Footer)

Plain text printed after the license list. Null clears it.

Preface (string) or Preface (null) (Preface)

Plain text printed before the license list. Null clears it.

Responses

Request samples

Content type
application/json
{
  • "footer": "string",
  • "preface": "string"
}

Response samples

Content type
application/json
{
  • "created_at": "2019-08-24T14:15:22Z",
  • "footer": "string",
  • "format": "string",
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6",
  • "preface": "string",
  • "updated_at": "2019-08-24T14:15:22Z"
}

notifications

Organization-wide rules

Authorizations:
bearerAuth
path Parameters
organization_id
required
string <uuid> (Organization Id)

Responses

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "total": 0
}

Add an organization-wide rule

Authorizations:
bearerAuth
path Parameters
organization_id
required
string <uuid> (Organization Id)
Request Body schema: application/json
required
channels
Array of strings (Channels)

Channels this rule adds. Never removes one somebody enabled.

email_recipients
Array of strings <email> (Email Recipients) <= 50 items [ items <email > ]

Addresses this rule adds.

is_active
boolean (Is Active)
Default: true
kinds
Array of strings (Kinds)

Notification kinds this rule covers. Empty means every kind.

Min Severity (string) or Min Severity (null) (Min Severity)

Matches this severity and everything above it. Omitted means severity is not part of the condition. A rule naming one does not fire for a notification that carries no severity at all.

name
required
string (Name) [ 1 .. 200 ] characters
Project Id (string) or Project Id (null) (Project Id)

One project. Omitted means every project in scope.

Responses

Request samples

Content type
application/json
{
  • "channels": [
    ],
  • "email_recipients": [
    ],
  • "is_active": true,
  • "kinds": [
    ],
  • "min_severity": "string",
  • "name": "string",
  • "project_id": "405d8375-3514-403b-8c43-83ae74cfe0e9"
}

Response samples

Content type
application/json
{
  • "channels": [
    ],
  • "created_at": "2019-08-24T14:15:22Z",
  • "email_recipients": [
    ],
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "is_active": true,
  • "kinds": [
    ],
  • "min_severity": "string",
  • "name": "string",
  • "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6",
  • "project_id": "405d8375-3514-403b-8c43-83ae74cfe0e9",
  • "team_id": "810007d0-bec5-486c-b5d1-28fcd8a079ba",
  • "updated_at": "2019-08-24T14:15:22Z"
}

Rules that reach one team, including the organization's own

Authorizations:
bearerAuth
path Parameters
team_id
required
string <uuid> (Team Id)

Responses

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "total": 0
}

Add a rule for one team

Authorizations:
bearerAuth
path Parameters
team_id
required
string <uuid> (Team Id)
Request Body schema: application/json
required
channels
Array of strings (Channels)

Channels this rule adds. Never removes one somebody enabled.

email_recipients
Array of strings <email> (Email Recipients) <= 50 items [ items <email > ]

Addresses this rule adds.

is_active
boolean (Is Active)
Default: true
kinds
Array of strings (Kinds)

Notification kinds this rule covers. Empty means every kind.

Min Severity (string) or Min Severity (null) (Min Severity)

Matches this severity and everything above it. Omitted means severity is not part of the condition. A rule naming one does not fire for a notification that carries no severity at all.

name
required
string (Name) [ 1 .. 200 ] characters
Project Id (string) or Project Id (null) (Project Id)

One project. Omitted means every project in scope.

Responses

Request samples

Content type
application/json
{
  • "channels": [
    ],
  • "email_recipients": [
    ],
  • "is_active": true,
  • "kinds": [
    ],
  • "min_severity": "string",
  • "name": "string",
  • "project_id": "405d8375-3514-403b-8c43-83ae74cfe0e9"
}

Response samples

Content type
application/json
{
  • "channels": [
    ],
  • "created_at": "2019-08-24T14:15:22Z",
  • "email_recipients": [
    ],
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "is_active": true,
  • "kinds": [
    ],
  • "min_severity": "string",
  • "name": "string",
  • "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6",
  • "project_id": "405d8375-3514-403b-8c43-83ae74cfe0e9",
  • "team_id": "810007d0-bec5-486c-b5d1-28fcd8a079ba",
  • "updated_at": "2019-08-24T14:15:22Z"
}

Remove a rule

Authorizations:
bearerAuth
path Parameters
rule_id
required
string <uuid> (Rule Id)

Responses

Response samples

Content type
application/problem+json
{
  • "detail": "string",
  • "instance": "string",
  • "status": 0,
  • "title": "string",
  • "type": "about:blank"
}

Paginated list of notifications for the authenticated user

Authorizations:
bearerAuth
query Parameters
unread_only
boolean (Unread Only)
Default: false
page
integer (Page) [ 1 .. 1000000 ]
Default: 1
page_size
integer (Page Size) [ 1 .. 200 ]
Default: 20

Responses

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "page": 0,
  • "page_size": 0,
  • "total": 0,
  • "unread_count": 0
}

Mark all of the caller's unread notifications as read

Authorizations:
bearerAuth

Responses

Response samples

Content type
application/problem+json
{
  • "detail": "string",
  • "instance": "string",
  • "status": 0,
  • "title": "string",
  • "type": "about:blank"
}

Unread notification count for the authenticated user (bell badge)

Authorizations:
bearerAuth

Responses

Response samples

Content type
application/json
{
  • "count": 0
}

Mark a single notification as read (idempotent)

Authorizations:
bearerAuth
path Parameters
notification_id
required
string <uuid> (Notification Id)

Responses

Response samples

Content type
application/problem+json
{
  • "detail": "string",
  • "instance": "string",
  • "status": 0,
  • "title": "string",
  • "type": "about:blank"
}

organization-verdicts

What this project is judged by for this component

Authorizations:
bearerAuth
path Parameters
project_id
required
string <uuid> (Project Id)
component_id
required
string <uuid> (Component Id)

Responses

Response samples

Content type
application/json
{
  • "component_id": "41afafe5-2a48-424a-baef-34c2ad44ef7b",
  • "justification": "string",
  • "project_id": "405d8375-3514-403b-8c43-83ae74cfe0e9",
  • "scope": "string",
  • "status": "string"
}

The organization's rulings, newest first

Rulings for one organization, readable by anybody inside it.

What each caller sees is not the same. The published reason goes to everybody, because it is what explains an inherited status on their own screen. The deliberation around it, and the names of the people involved, go only to callers who could have written them: a note an administrator made while deciding is not part of explaining the outcome.

Authorizations:
bearerAuth
path Parameters
organization_id
required
string <uuid> (Organization Id)
query Parameters
Array of Status (strings) or Status (null) (Status)
page
integer (Page) [ 1 .. 1000000 ]
Default: 1
page_size
integer (Page Size) [ 1 .. 200 ]
Default: 50

Responses

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "page": 1,
  • "page_size": 50,
  • "total": 0
}

Start an organization-wide ruling on a component

Authorizations:
bearerAuth
path Parameters
organization_id
required
string <uuid> (Organization Id)
Request Body schema: application/json
required
component_id
required
string <uuid> (Component Id)
justification
required
string (Justification) >= 10 characters

Why this is being ruled on centrally. Required: this answer reaches every project that has not decided for itself, and it is the sentence people will ask about later.

Responses

Request samples

Content type
application/json
{
  • "component_id": "41afafe5-2a48-424a-baef-34c2ad44ef7b",
  • "justification": "stringstri"
}

Response samples

Content type
application/json
{
  • "component_id": "41afafe5-2a48-424a-baef-34c2ad44ef7b",
  • "created_at": "2019-08-24T14:15:22Z",
  • "decided_at": "2019-08-24T14:15:22Z",
  • "decided_by_user_id": "c750fc7f-0b29-4c93-a0d7-9578b1a62969",
  • "decision_note": "string",
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "justification": "string",
  • "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6",
  • "requested_by_user_id": "ed1d5170-d6e9-4e60-8754-78ea9640e9db",
  • "status": "string",
  • "updated_at": "2019-08-24T14:15:22Z",
  • "version": 0
}

Move an organization ruling along

Authorizations:
bearerAuth
path Parameters
verdict_id
required
string <uuid> (Verdict Id)
Request Body schema: application/json
required
Note (string) or Note (null) (Note)

Optional reasoning, kept whichever way it went.

status
required
string (Status)

under_review, approved or rejected.

Responses

Request samples

Content type
application/json
{
  • "note": "string",
  • "status": "string"
}

Response samples

Content type
application/json
{
  • "component_id": "41afafe5-2a48-424a-baef-34c2ad44ef7b",
  • "created_at": "2019-08-24T14:15:22Z",
  • "decided_at": "2019-08-24T14:15:22Z",
  • "decided_by_user_id": "c750fc7f-0b29-4c93-a0d7-9578b1a62969",
  • "decision_note": "string",
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "justification": "string",
  • "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6",
  • "requested_by_user_id": "ed1d5170-d6e9-4e60-8754-78ea9640e9db",
  • "status": "string",
  • "updated_at": "2019-08-24T14:15:22Z",
  • "version": 0
}

projects

List projects visible to the caller

Authorizations:
bearerAuth
query Parameters
Team Id (string) or Team Id (null) (Team Id)
include_archived
boolean (Include Archived)
Default: false
Q (string) or Q (null) (Q)
Business Unit (string) or Business Unit (null) (Business Unit)

Narrow to one owning part of the organization. Omitted or blank leaves every project in, including the ones that have not set it.

Distribution Model (string) or Distribution Model (null) (Distribution Model)

Narrow to one distribution model, or 'unset' for the projects that have not said how they ship. Omitted leaves every project in.

page
integer (Page) [ 1 .. 1000000 ]
Default: 1
size
integer (Size) [ 1 .. 100 ]
Default: 20

Responses

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "page": 0,
  • "size": 0,
  • "total": 0
}

Create a project (auth required, role >= developer)

Authorizations:
bearerAuth
Request Body schema: application/json
required
Ai Usage Context (string) or Ai Usage Context (null) (Ai Usage Context)

How this project intends to use the AI models it carries. Narrows a model or dataset license to the conditions that bind that use ('internal', 'product', 'redistribute', 'outputs-only'). Omit to judge against the full terms, which is the conservative reading.

Business Unit (string) or Business Unit (null) (Business Unit)

Which part of the organization owns this project. Free text: a division, a cost centre and a squad are the same slot to different organizations. Used to narrow the portfolio list.

Declared License (string) or Declared License (null) (Declared License)

SPDX id or expression the project itself is distributed under ('Apache-2.0', 'MIT OR Apache-2.0'). Drives the outbound-license conflict verdicts on the Licenses tab. Omit or leave blank when the project does not declare one — no verdicts are produced, which is not the same as a clean result.

Default Branch (string) or Default Branch (null) (Default Branch)
Description (string) or Description (null) (Description)
Distribution Model (string) or Distribution Model (null) (Distribution Model)

How this software reaches the people who use it: 'internal', 'saas', 'binary', 'source' or 'embedded'. Omit when it has not been decided, which is judged as though it ships every way.

Git Url (string) or Git Url (null) (Git Url)
name
required
string (Name) [ 1 .. 255 ] characters
Owner Contact (string) or Owner Contact (null) (Owner Contact)

Who to ask about this project. A name, a team alias or an address.

slug
required
string (Slug) [ 1 .. 64 ] characters
team_id
required
string <uuid> (Team Id)
visibility
string (Visibility)
Default: "team"
Enum: "team" "organization"

Responses

Request samples

Content type
application/json
{
  • "ai_usage_context": "internal",
  • "business_unit": "string",
  • "declared_license": "string",
  • "default_branch": "string",
  • "description": "string",
  • "distribution_model": "internal",
  • "git_url": "string",
  • "name": "string",
  • "owner_contact": "string",
  • "slug": "string",
  • "team_id": "810007d0-bec5-486c-b5d1-28fcd8a079ba",
  • "visibility": "team"
}

Response samples

Content type
application/json
{
  • "ai_usage_context": "internal",
  • "archived_at": "2019-08-24T14:15:22Z",
  • "business_unit": "string",
  • "created_at": "2019-08-24T14:15:22Z",
  • "created_by_user_id": "209f54c4-4c33-43bc-9c6a-ef4c65ad7473",
  • "created_by_user_name": "string",
  • "declared_license": "string",
  • "default_branch": "string",
  • "description": "string",
  • "distribution_model": "internal",
  • "git_url": "string",
  • "group_path": [
    ],
  • "has_git_credential": false,
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "last_scan_at": "2019-08-24T14:15:22Z",
  • "latest_scan_id": "df070bcd-0cf7-4ee8-8a3b-eea17ac86447",
  • "latest_scan_status": "queued",
  • "license_category_summary": {
    },
  • "name": "string",
  • "owner_contact": "string",
  • "release_count": 0,
  • "scan_count": 0,
  • "severity_summary": {
    },
  • "slug": "string",
  • "team_id": "810007d0-bec5-486c-b5d1-28fcd8a079ba",
  • "team_name": "string",
  • "updated_at": "2019-08-24T14:15:22Z",
  • "visibility": "team"
}

The filtered project portfolio list as CSV

The same rows the list endpoint would return, without the paging.

Filters and team-scoping are applied by list_projects itself rather than by a second query, so the file and the screen cannot disagree.

Authorizations:
bearerAuth
query Parameters
Team Id (string) or Team Id (null) (Team Id)
include_archived
boolean (Include Archived)
Default: false
Q (string) or Q (null) (Q)
Business Unit (string) or Business Unit (null) (Business Unit)
Distribution Model (string) or Distribution Model (null) (Distribution Model)

Responses

Response samples

Content type
application/problem+json
{
  • "detail": "string",
  • "instance": "string",
  • "status": 0,
  • "title": "string",
  • "type": "about:blank"
}

Archive (soft-delete) the project (developer and above)

Authorizations:
bearerAuth
path Parameters
project_id
required
string <uuid> (Project Id)

Responses

Response samples

Content type
application/problem+json
{
  • "detail": "string",
  • "instance": "string",
  • "status": 0,
  • "title": "string",
  • "type": "about:blank"
}

Read one project (IDOR-safe; 403 if not a team member)

Authorizations:
bearerAuth
path Parameters
project_id
required
string <uuid> (Project Id)

Responses

Response samples

Content type
application/json
{
  • "ai_usage_context": "internal",
  • "archived_at": "2019-08-24T14:15:22Z",
  • "business_unit": "string",
  • "created_at": "2019-08-24T14:15:22Z",
  • "created_by_user_id": "209f54c4-4c33-43bc-9c6a-ef4c65ad7473",
  • "created_by_user_name": "string",
  • "declared_license": "string",
  • "default_branch": "string",
  • "description": "string",
  • "distribution_model": "internal",
  • "git_url": "string",
  • "group_path": [
    ],
  • "has_git_credential": false,
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "last_scan_at": "2019-08-24T14:15:22Z",
  • "latest_scan_id": "df070bcd-0cf7-4ee8-8a3b-eea17ac86447",
  • "latest_scan_status": "queued",
  • "license_category_summary": {
    },
  • "name": "string",
  • "owner_contact": "string",
  • "release_count": 0,
  • "scan_count": 0,
  • "severity_summary": {
    },
  • "slug": "string",
  • "team_id": "810007d0-bec5-486c-b5d1-28fcd8a079ba",
  • "team_name": "string",
  • "updated_at": "2019-08-24T14:15:22Z",
  • "visibility": "team"
}

Update mutable project fields (role >= team_admin)

Authorizations:
bearerAuth
path Parameters
project_id
required
string <uuid> (Project Id)
Request Body schema: application/json
required
Ai Usage Context (string) or Ai Usage Context (null) (Ai Usage Context)

How this project intends to use its AI models ('internal', 'product', 'redistribute', 'outputs-only'). Send an empty string to clear it and judge against the full terms; omit the field to leave it unchanged.

Business Unit (string) or Business Unit (null) (Business Unit)

Which part of the organization owns this project. Send an empty string to clear it; omit the field to leave it unchanged.

clear_git_credential
boolean (Clear Git Credential)
Default: false

Set true to remove a stored git credential (column → NULL). Cannot be combined with a non-empty git_credential.

Declared License (string) or Declared License (null) (Declared License)

SPDX id or expression the project is distributed under. Send an empty string to remove an existing declaration; omit the field to leave it unchanged.

Default Branch (string) or Default Branch (null) (Default Branch)
Description (string) or Description (null) (Description)
Distribution Model (string) or Distribution Model (null) (Distribution Model)

How this software reaches its users ('internal', 'saas', 'binary', 'source', 'embedded'). Send an empty string to clear it and judge as though it ships every way; omit to leave it unchanged.

Git Credential (string) or Git Credential (null) (Git Credential)

Write-only plaintext git credential (PAT / deploy token) for cloning a private repo. Encrypted at rest; NEVER returned in any response. Provide a non-empty value to set/rotate it. Omit to leave it unchanged. Use clear_git_credential: true to remove it.

Git Url (string) or Git Url (null) (Git Url)
Name (string) or Name (null) (Name)
Owner Contact (string) or Owner Contact (null) (Owner Contact)

Who to ask about this project. Send an empty string to clear it; omit the field to leave it unchanged.

Visibility (string) or Visibility (null) (Visibility)

Responses

Request samples

Content type
application/json
{
  • "ai_usage_context": "internal",
  • "business_unit": "string",
  • "clear_git_credential": false,
  • "declared_license": "string",
  • "default_branch": "string",
  • "description": "string",
  • "distribution_model": "internal",
  • "git_credential": "string",
  • "git_url": "string",
  • "name": "string",
  • "owner_contact": "string",
  • "visibility": "team"
}

Response samples

Content type
application/json
{
  • "ai_usage_context": "internal",
  • "archived_at": "2019-08-24T14:15:22Z",
  • "business_unit": "string",
  • "created_at": "2019-08-24T14:15:22Z",
  • "created_by_user_id": "209f54c4-4c33-43bc-9c6a-ef4c65ad7473",
  • "created_by_user_name": "string",
  • "declared_license": "string",
  • "default_branch": "string",
  • "description": "string",
  • "distribution_model": "internal",
  • "git_url": "string",
  • "group_path": [
    ],
  • "has_git_credential": false,
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "last_scan_at": "2019-08-24T14:15:22Z",
  • "latest_scan_id": "df070bcd-0cf7-4ee8-8a3b-eea17ac86447",
  • "latest_scan_status": "queued",
  • "license_category_summary": {
    },
  • "name": "string",
  • "owner_contact": "string",
  • "release_count": 0,
  • "scan_count": 0,
  • "severity_summary": {
    },
  • "slug": "string",
  • "team_id": "810007d0-bec5-486c-b5d1-28fcd8a079ba",
  • "team_name": "string",
  • "updated_at": "2019-08-24T14:15:22Z",
  • "visibility": "team"
}

People this project's work may be assigned to

Who may be named as the owner of a finding or an obligation here.

Addressed by project rather than by team because that is what a caller holds: the screen is a project's finding list, and asking it for a team id would mean handing out team ids to look work up by. The team is derived here, and access is the project's own rule.

developer for the same reason the assignment PATCH is: a caller who may perform an assignment has to be able to compose one, and a list gated higher would leave the write reachable only by somebody who already knew the id.

The set is exactly what services.assignee will accept, because both go through one predicate rather than two copies of three conditions.

A premise the group-hierarchy cascade broke, and this PR closed

Deriving the team from the project used to be safe because reaching a project meant being on its team, full stop. A later change made get_project above cascade-aware (it now runs its team gate through core.authz.assert_team_access / can_access_group, same as every other single-resource surface), so that premise no longer holds unconditionally: with the cascade flag on, an actor can reach get_project through an ANCESTOR group's direct membership, without being a direct member of project.team_id itself.

services.assignee (the picker below, and the write-time eligibility check the assignment PATCH uses) already anticipated this: PR 2-C widened it to the project's team's ancestors when :func:core.config.group_cascade_enabled is on, specifically so this route would not need a second, coordinated change once get_project itself was cascaded. It was. So this route's set stays exactly what services.assignee accepts, in both flag states, with no change needed here.

Organization-wide visibility (a different widening, not the group cascade) would still end the "reaching a project means being on its team" premise for get_project itself. Somebody on another team could then read the project, and this route would hand them its members, which is the enumeration the tests here refuse. Whoever enables it has to decide what this endpoint does: most likely keep it on team membership rather than on project access, since being allowed to read a project's findings is not the same as being allowed to list the people on it.

core.authz.team_scope_filter carries a pointer back here, because that is the file the change lands in.

Authorizations:
bearerAuth
path Parameters
project_id
required
string <uuid> (Project Id)

Responses

Response samples

Content type
application/json
{
  • "members": [
    ],
  • "total": 0
}

Paginated component list for the project's latest scan

Authorizations:
bearerAuth
path Parameters
project_id
required
string <uuid> (Project Id)
query Parameters
limit
integer (Limit) [ 1 .. 500 ]
Default: 50
offset
integer (Offset) >= 0
Default: 0
Search (string) or Search (null) (Search)
Array of Severity (strings) or Severity (null) (Severity)
Array of License Category (strings) or License Category (null) (License Category)
Direct (boolean) or Direct (null) (Direct)

W2 #31 — Direct/Transitive toggle. true keeps only direct deps (graph depth 1), false only transitive (or graph-less) deps. Omit to include both. commercial-SCA equivalent of the 'Dependency type' facet.

Array of Dependency Scope (strings) or Dependency Scope (null) (Dependency Scope)

W2 #31 — commercial-SCA-style 'Usage' facet. Repeatable; accepted values: required, optional, unspecified (the NULL-scope bucket — common for SBOMs that don't encode scope). Unknown values are dropped, so a query that filters only by unknown values returns an empty page (not a 422). Omit to include all.

Eol (boolean) or Eol (null) (Eol)

Phase M — end-of-life facet. true keeps only components whose release cycle is past its published end-of-life (endoflife.date); false keeps everything else, including untracked components. Omit to include both. Boolean mirrors the KEV filter UX.

Outdated (boolean) or Outdated (null) (Outdated)

Version-currency facet (sibling of the EOL filter). true keeps only components behind the newest patch of their release line (endoflife.date); false keeps everything else, including current, unknown and untracked components. Omit to include both. Boolean mirrors the KEV filter UX.

Malicious (boolean) or Malicious (null) (Malicious)

Known-malicious facet (#26). true keeps only components the vendored OSV MAL- snapshot lists as published-to-attack; false keeps everything else, INCLUDING components that were never assessed. Omit to include both. Note the asymmetry with eol: this axis is not a vulnerability — a flagged package is removed and its reachable credentials rotated, not upgraded — so it creates no finding and never enters severity counts.

sort
string (Sort) ^(name|severity|license)$
Default: "name"
order
string (Order) ^(asc|desc)$
Default: "asc"
Scan Id (string) or Scan Id (null) (Scan Id)

Optional release-snapshot anchor. Read this surface as of ONE specific succeeded scan instead of the project's current state. Must belong to this project and be succeeded, else 404. Takes precedence over release.

Release (string) or Release (null) (Release)

Optional version anchor — read this surface as of the release carrying this label (e.g. '4.0'). Equivalent to looking the label up on /releases?release= and pinning the scan_id it returns, but as one permanent URL. Matched exactly, whitespace trimmed. Unknown label → 404, and so does a label in a project you cannot read. Ignored when scan_id is also given.

Responses

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "limit": 0,
  • "offset": 0,
  • "total": 0
}

The filtered component list as CSV

The same rows the list endpoint would return, without the paging.

Filters are applied by the list service itself rather than by a second query, so the file and the screen cannot disagree, and the cross-team check the list performs is the one this performs.

sort/order are still accepted and validated but no longer decide the exported rows' order (#463): the export walks a fixed key (the component_version id) instead, so a bill of materials that reaches real depth does not pay for OFFSET's cost growing with it.

Authorizations:
bearerAuth
path Parameters
project_id
required
string <uuid> (Project Id)
query Parameters
Search (string) or Search (null) (Search)
Array of Severity (strings) or Severity (null) (Severity)
Array of License Category (strings) or License Category (null) (License Category)
Direct (boolean) or Direct (null) (Direct)
Array of Dependency Scope (strings) or Dependency Scope (null) (Dependency Scope)
Eol (boolean) or Eol (null) (Eol)
Outdated (boolean) or Outdated (null) (Outdated)
Malicious (boolean) or Malicious (null) (Malicious)
sort
string (Sort) ^(name|severity|license)$
Default: "name"
order
string (Order) ^(asc|desc)$
Default: "asc"
Scan Id (string) or Scan Id (null) (Scan Id)

Optional release-snapshot anchor. Read this surface as of ONE specific succeeded scan instead of the project's current state. Must belong to this project and be succeeded, else 404. Takes precedence over release.

Release (string) or Release (null) (Release)

Optional version anchor — read this surface as of the release carrying this label (e.g. '4.0'). Equivalent to looking the label up on /releases?release= and pinning the scan_id it returns, but as one permanent URL. Matched exactly, whitespace trimmed. Unknown label → 404, and so does a label in a project you cannot read. Ignored when scan_id is also given.

Responses

Response samples

Content type
application/problem+json
{
  • "detail": "string",
  • "instance": "string",
  • "status": 0,
  • "title": "string",
  • "type": "about:blank"
}

Resolved dependency graph (nodes + edges) of a scan snapshot

Authorizations:
bearerAuth
path Parameters
project_id
required
string <uuid> (Project Id)
query Parameters
Scan Id (string) or Scan Id (null) (Scan Id)

Optional release-snapshot anchor. Read this surface as of ONE specific succeeded scan instead of the project's current state. Must belong to this project and be succeeded, else 404. Takes precedence over release.

Release (string) or Release (null) (Release)

Optional version anchor — read this surface as of the release carrying this label (e.g. '4.0'). Equivalent to looking the label up on /releases?release= and pinning the scan_id it returns, but as one permanent URL. Matched exactly, whitespace trimmed. Unknown label → 404, and so does a label in a project you cannot read. Ignored when scan_id is also given.

Responses

Response samples

Content type
application/json
{
  • "edge_count": 2,
  • "edges": [
    ],
  • "node_cap": 5000,
  • "node_count": 3,
  • "nodes": [
    ],
  • "scan_id": "3c15c82f-c409-4f5f-b7d9-92bca8cc1f7f",
  • "truncated": false
}

Diff two release snapshots (succeeded scans) of the project

Authorizations:
bearerAuth
path Parameters
project_id
required
string <uuid> (Project Id)
query Parameters
base
required
string <uuid> (Base)

Base snapshot scan id (typically the OLDER release, e.g. v0.1). Must belong to this project and be succeeded, else 404 (existence-hide).

target
required
string <uuid> (Target)

Target snapshot scan id (typically the NEWER release, e.g. v0.2). Must belong to this project and be succeeded, else 404. base == target is allowed and yields an all-empty diff.

Responses

Response samples

Content type
application/json
{
  • "base": {
    },
  • "components": {
    },
  • "licenses": {
    },
  • "summary": {
    },
  • "target": {
    },
  • "truncated": false,
  • "vulnerabilities": {
    }
}

Risk score, gate verdict, KEV deadlines, approvals and trend (W17 band)

The governance band's payload — five signals the page already owns.

Every number is produced by the service that owns it elsewhere in the product (risk score, evaluate_gate, the KEV bucket, the approvals count), so the band cannot disagree with the tab below it. See services.project_governance_service.

Same access contract as /overview: a project on a team the caller does not belong to is a 403 and a missing one a 404, both as problem+json.

Authorizations:
bearerAuth
path Parameters
project_id
required
string <uuid> (Project Id)

Responses

Response samples

Content type
application/json
{
  • "gate": {
    },
  • "kev_sla": {
    },
  • "pending_approvals": 0,
  • "project_id": "405d8375-3514-403b-8c43-83ae74cfe0e9",
  • "risk_score": 100,
  • "scanned": true,
  • "trend": [
    ]
}

Aggregated risk / scan picture for the project (Overview tab)

Authorizations:
bearerAuth
path Parameters
project_id
required
string <uuid> (Project Id)
query Parameters
Scan Id (string) or Scan Id (null) (Scan Id)

Optional release-snapshot anchor. Read this surface as of ONE specific succeeded scan instead of the project's current state. Must belong to this project and be succeeded, else 404. Takes precedence over release.

Release (string) or Release (null) (Release)

Optional version anchor — read this surface as of the release carrying this label (e.g. '4.0'). Equivalent to looking the label up on /releases?release= and pinning the scan_id it returns, but as one permanent URL. Matched exactly, whitespace trimmed. Unknown label → 404, and so does a label in a project you cannot read. Ignored when scan_id is also given.

Responses

Response samples

Content type
application/json
{
  • "component_outcome": "string",
  • "current_user_role": "super_admin",
  • "eol_count": 0,
  • "has_git_credential": false,
  • "last_scan_at": "2019-08-24T14:15:22Z",
  • "last_succeeded_scan_at": "2019-08-24T14:15:22Z",
  • "license_distribution": {
    },
  • "license_score": 100,
  • "malicious_count": 0,
  • "outdated_count": 0,
  • "project_id": "405d8375-3514-403b-8c43-83ae74cfe0e9",
  • "project_name": "string",
  • "recent_scans": [
    ],
  • "risk_score": 100,
  • "security_score": 100,
  • "severity_distribution": {
    },
  • "total_components": 0
}

List the project's release snapshots (succeeded scans, newest-first)

Authorizations:
bearerAuth
path Parameters
project_id
required
string <uuid> (Project Id)
query Parameters
page
integer (Page) [ 1 .. 1000000 ]
Default: 1
size
integer (Size) [ 1 .. 100 ]
Default: 20
Release (string) or Release (null) (Release)

Optional version label filter (e.g. '4.0'), matched exactly against the scan's metadata.release with surrounding whitespace trimmed. This is the lookup for "which snapshot is version 4.0?": a label identifies at most one live snapshot, so the filtered list carries one row whose scan_id you then pin on the detail endpoints via ?scan_id=. An unknown label returns an empty list, not a 404.

Ref (string) or Ref (null) (Ref)

Optional branch filter. Accepts a bare branch (main) or a fully-qualified ref (refs/heads/main, refs/pull/12/merge), normalized the same way scan triggers normalize it, so a branch reaches its own snapshots either way.

Responses

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "page": 1,
  • "size": 20,
  • "total": 1
}

Trigger a scan for the project (queues a Celery task; returns 202 Accepted)

Authorizations:
bearerAuth
path Parameters
project_id
required
string <uuid> (Project Id)
Request Body schema: application/json
required
kind
string (Kind)
Default: "source"
Enum: "source" "container" "sbom"
object (Metadata)

Responses

Request samples

Content type
application/json
{
  • "kind": "source",
  • "metadata": { }
}

Response samples

Content type
application/json
{
  • "celery_task_id": "string",
  • "completed_at": "2019-08-24T14:15:22Z",
  • "created_at": "2019-08-24T14:15:22Z",
  • "current_step": "string",
  • "error_message": "string",
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "kind": "source",
  • "metadata": { },
  • "progress_percent": 0,
  • "project_id": "405d8375-3514-403b-8c43-83ae74cfe0e9",
  • "project_name": "string",
  • "project_slug": "string",
  • "ref": "string",
  • "release": "string",
  • "requested_by_user_id": "ed1d5170-d6e9-4e60-8754-78ea9640e9db",
  • "started_at": "2019-08-24T14:15:22Z",
  • "status": "queued",
  • "superseded_at": "2019-08-24T14:15:22Z",
  • "updated_at": "2019-08-24T14:15:22Z"
}

Upload a .zip of local source for scanning (auth required, role >= developer)

Authorizations:
bearerAuth
path Parameters
project_id
required
string <uuid> (Project Id)
Request Body schema: multipart/form-data
required
upload
required
string <application/octet-stream> (Upload)

A .zip archive of the project source tree.

Responses

Response samples

Content type
application/json
{
  • "archive_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479"
}

Whether this project's webhook is active (role >= team_admin)

Configured or not, and for which provider. Never the secret.

Split from the issue route so the question a screen asks has an answer that carries no credential bytes. There is no route that returns the secret after issuance, and this is the one somebody would be tempted to add it to.

Authorizations:
bearerAuth
path Parameters
project_id
required
string <uuid> (Project Id)

Responses

Response samples

Content type
application/json
{
  • "configured": true,
  • "project_id": "405d8375-3514-403b-8c43-83ae74cfe0e9",
  • "provider": "github"
}

Issue this project's webhook shared secret (role >= team_admin)

Turn the webhook on, and hand back the secret once.

This replaced an operator running an UPDATE by hand. The column holds ciphertext now and SQL cannot produce it, so the procedure had to move into the product; having moved, it gets what the SQL never had, which is a permission check, an audit row, and one path that sets both fields.

team_admin rather than developer: this decides whether a repository can make this deployment start scans, and it is the same level that edits the project's other settings.

Calling it again replaces the secret. The response says so in replaced_existing, because the old value stops being accepted the moment this returns and the deliveries that were working will fail until the new one reaches the SCM. Somebody who meant to read the current value and found there is no such route would otherwise learn this by breaking their own integration.

Authorizations:
bearerAuth
path Parameters
project_id
required
string <uuid> (Project Id)
Request Body schema: application/json
required
provider
required
string (Provider)
Enum: "github" "gitlab"

Which SCM will send the deliveries. It decides the header schema the gateway applies: GitHub signs the body and sends X-Hub-Signature-256, GitLab sends the secret itself as X-Gitlab-Token. A project configured for one and receiving from the other has every delivery refused.

Responses

Request samples

Content type
application/json
{
  • "provider": "github"
}

Response samples

Content type
application/json
{
  • "issued_at": "2019-08-24T14:15:22Z",
  • "project_id": "405d8375-3514-403b-8c43-83ae74cfe0e9",
  • "provider": "github",
  • "replaced_existing": true,
  • "secret": "string"
}

Create many projects in one request (auth required, role >= developer)

Register a set of repositories as projects.

Rows are independent: one the caller may not create, or one whose slug the team already uses, is reported and the rest still land. Onboarding an organization means creating a project per repository, and failing the whole request over a single repository would make that unusable.

Status is 201 when every row succeeded and 207 when any did not, so a script can branch on the status line alone. A row whose project already exists counts as success: re-running to finish an interrupted onboarding is the normal path, and reporting that as failure would make every re-run look broken.

Authorizations:
bearerAuth
Request Body schema: application/json
required
required
Array of objects (Projects) [ 1 .. 200 ] items

Projects to create, at most 200. Each entry is the same body POST /v1/projects takes. Rows are processed in order and independently: a row that fails does not undo the rows before it.

Array ([ 1 .. 200 ] items)
Ai Usage Context (string) or Ai Usage Context (null) (Ai Usage Context)

How this project intends to use the AI models it carries. Narrows a model or dataset license to the conditions that bind that use ('internal', 'product', 'redistribute', 'outputs-only'). Omit to judge against the full terms, which is the conservative reading.

Business Unit (string) or Business Unit (null) (Business Unit)

Which part of the organization owns this project. Free text: a division, a cost centre and a squad are the same slot to different organizations. Used to narrow the portfolio list.

Declared License (string) or Declared License (null) (Declared License)

SPDX id or expression the project itself is distributed under ('Apache-2.0', 'MIT OR Apache-2.0'). Drives the outbound-license conflict verdicts on the Licenses tab. Omit or leave blank when the project does not declare one — no verdicts are produced, which is not the same as a clean result.

Default Branch (string) or Default Branch (null) (Default Branch)
Description (string) or Description (null) (Description)
Distribution Model (string) or Distribution Model (null) (Distribution Model)

How this software reaches the people who use it: 'internal', 'saas', 'binary', 'source' or 'embedded'. Omit when it has not been decided, which is judged as though it ships every way.

Git Url (string) or Git Url (null) (Git Url)
name
required
string (Name) [ 1 .. 255 ] characters
Owner Contact (string) or Owner Contact (null) (Owner Contact)

Who to ask about this project. A name, a team alias or an address.

slug
required
string (Slug) [ 1 .. 64 ] characters
team_id
required
string <uuid> (Team Id)
visibility
string (Visibility)
Default: "team"
Enum: "team" "organization"

Responses

Request samples

Content type
application/json
{
  • "projects": [
    ]
}

Response samples

Content type
application/json
{
  • "all_succeeded": true,
  • "already_existed": 0,
  • "created": 0,
  • "failed": 0,
  • "failed_by_status": {
    },
  • "rows": [
    ],
  • "total": 0
}

compliance

Unified Compliance grid (licenses × obligations) for the project's latest succeeded scan

Authorizations:
bearerAuth
path Parameters
project_id
required
string <uuid> (Project Id)
query Parameters
limit
integer (Limit) [ 1 .. 500 ]
Default: 50
offset
integer (Offset) >= 0
Default: 0
Array of Category (strings) or Category (null) (Category)

Filter rows by license category. Repeat the parameter to OR-join multiple values (e.g. ?category=forbidden&category=conditional).

Array of Kind (strings) or Kind (null) (Kind)

Filter rows to licenses that carry at least one obligation of the given kind. Repeat to OR-join.

Search (string) or Search (null) (Search)

Substring match against SPDX id and license name. LIKE metacharacters are escaped server-side.

Has Obligations (boolean) or Has Obligations (null) (Has Obligations)

When true, return only licenses that carry at least one obligation row. When false, return only licenses with NONE. Ignored when kind is also given.

sort
string (Sort) ^(category|license_name|spdx_id|affected_coun...
Default: "category"
order
string (Order) ^(asc|desc)$
Default: "desc"
Conflict (string) or Conflict (null) (Conflict)

Filter to licenses carrying this outbound-conflict verdict (gap #27). Matches nothing when the project declares no outbound license, since nothing is assessed without one. Omit to list all.

Scan Id (string) or Scan Id (null) (Scan Id)

Optional release-snapshot anchor. Read this surface as of ONE specific succeeded scan instead of the project's current state. Must belong to this project and be succeeded, else 404. Takes precedence over release.

Release (string) or Release (null) (Release)

Optional version anchor — read this surface as of the release carrying this label (e.g. '4.0'). Equivalent to looking the label up on /releases?release= and pinning the scan_id it returns, but as one permanent URL. Matched exactly, whitespace trimmed. Unknown label → 404, and so does a label in a project you cannot read. Ignored when scan_id is also given.

Responses

Response samples

Content type
application/json
{
  • "conflict_summary": {
    },
  • "declared_license": "string",
  • "distribution": {
    },
  • "generated_at": "2019-08-24T14:15:22Z",
  • "items": [
    ],
  • "limit": 1,
  • "offset": 0,
  • "total": 0
}

policy-gate

Evaluate the build-gate verdict for the project's current-state scan

Authorizations:
bearerAuth
path Parameters
project_id
required
string <uuid> (Project Id)
query Parameters
Ref (string) or Ref (null) (Ref)

Optional branch anchor: evaluate against the newest succeeded scan of this normalized git ref (main, pr-12) instead of the project's main line. A CI job should pass its own ref so its verdict cannot be decided by a scan of a different branch. Exact: a ref with no succeeded scan yields the no-signal pass, never another branch's findings. Ignored when scan_id is given, which is more specific.

Scan Id (string) or Scan Id (null) (Scan Id)

Optional release-snapshot anchor. Read this surface as of ONE specific succeeded scan instead of the project's current state. Must belong to this project and be succeeded, else 404. Takes precedence over release.

Release (string) or Release (null) (Release)

Optional version anchor — read this surface as of the release carrying this label (e.g. '4.0'). Equivalent to looking the label up on /releases?release= and pinning the scan_id it returns, but as one permanent URL. Matched exactly, whitespace trimmed. Unknown label → 404, and so does a label in a project you cannot read. Ignored when scan_id is also given.

Responses

Response samples

Content type
application/json
{
  • "component_outcome": "string",
  • "critical_cve_count": 0,
  • "eol_gate_count": 0,
  • "eol_gate_enabled": false,
  • "eol_on_missing_data": "allow",
  • "eol_outcome": "not_configured",
  • "epss_gate_count": 0,
  • "epss_on_missing_data": "allow",
  • "epss_outcome": "not_configured",
  • "epss_threshold": 1,
  • "evaluated_at": "2019-08-24T14:15:22Z",
  • "forbidden_license_count": 0,
  • "gate": "pass",
  • "kev_gate_count": 0,
  • "kev_gate_enabled": false,
  • "kev_on_missing_data": "allow",
  • "kev_outcome": "not_configured",
  • "malicious_component_count": 0,
  • "malicious_gate_enforced": true,
  • "malicious_scan_assessed": false,
  • "project_id": "405d8375-3514-403b-8c43-83ae74cfe0e9",
  • "reachable_critical_cve_count": 0,
  • "reachable_gate_enforced": false,
  • "reason": "string",
  • "scan_id": "9a59f0f5-5572-476d-a7fc-c960ef43a5af"
}

The project's open findings as a SARIF 2.1.0 document

Emit the scan's open findings in the format code-scanning tools ingest.

Access control, snapshot anchoring and ref normalisation are the gate endpoint's, deliberately: this is the same data in another representation, so a caller who may read the verdict may read this, and a caller who may not gets the same existence-hiding 404.

A project with no succeeded scan returns an empty-but-valid document rather than a 404. That is what a CI job must be able to upload: an empty run is how code scanning learns that previously-reported alerts are gone, and a 404 here would either fail the job or leave stale alerts standing forever.

Authorizations:
bearerAuth
path Parameters
project_id
required
string <uuid> (Project Id)
query Parameters
Ref (string) or Ref (null) (Ref)

Optional branch anchor, resolved exactly as for the gate verdict: a CI job should pass its own ref so the document it uploads describes its own branch rather than the project's main line.

Scan Id (string) or Scan Id (null) (Scan Id)

Optional release-snapshot anchor. Read this surface as of ONE specific succeeded scan instead of the project's current state. Must belong to this project and be succeeded, else 404. Takes precedence over release.

Release (string) or Release (null) (Release)

Optional version anchor — read this surface as of the release carrying this label (e.g. '4.0'). Equivalent to looking the label up on /releases?release= and pinning the scan_id it returns, but as one permanent URL. Matched exactly, whitespace trimmed. Unknown label → 404, and so does a label in a project you cannot read. Ignored when scan_id is also given.

Responses

Response samples

Content type
application/problem+json
{
  • "detail": "string",
  • "instance": "string",
  • "status": 0,
  • "title": "string",
  • "type": "about:blank"
}

Render an SCA Markdown report and (optionally) post it to a GitHub PR

Authorizations:
bearerAuth
path Parameters
scan_id
required
string <uuid> (Scan Id)
Request Body schema: application/json
required
dry_run
boolean (Dry Run)
Default: false

When true the endpoint builds the Markdown comment but does not call GitHub. Useful for local CI rehearsals and used by the default integration tests so they do not require network access.

pr_number
required
integer (Pr Number) [ 1 .. 10000000 ]

The pull request number on GitHub, or the merge request IID on GitLab. The IID is the per-project number in the MR's URL, not the instance-wide id.

provider
string (Provider)
Default: "github"
Enum: "github" "gitlab"

Which forge to comment on. Defaults to github, which is what every caller was before GitLab existed here, so an existing integration keeps working unchanged. The response says when the default was used: a GitLab pipeline that omits this field would otherwise get a GitHub error about a project it never named.

repo_full_name
required
string (Repo Full Name) [ 3 .. 140 ] characters

The project's full path. The name says repo because the whole surface speaks GitHub, down to post-pr-comment in the path; the format is the provider's. GitHub takes owner/repo and nothing longer. GitLab takes the full path including any groups it nests under, such as group/subgroup/project. Validated per provider so the service never builds an API path out of a segment count it did not expect.

Responses

Request samples

Content type
application/json
{
  • "dry_run": false,
  • "pr_number": 1,
  • "provider": "github",
  • "repo_full_name": "string"
}

Response samples

Content type
application/json
{
  • "body_preview": "string",
  • "comment_id": 0,
  • "comment_url": "string",
  • "gate": "pass",
  • "provider": "github",
  • "provider_assumed": false,
  • "status": "posted"
}

obligations

Compose a NOTICE attribution body for the project's latest scan (or a pinned one)

Authorizations:
bearerAuth
path Parameters
project_id
required
string <uuid> (Project Id)
query Parameters
format
string (Format) ^(text|markdown|html)$
Default: "text"

Output format. text returns text/plain, markdown returns text/markdown, html returns a self-contained text/html document.

download
boolean (Download)
Default: false

When true, set Content-Disposition: attachment so browsers save the body as a file. Default is inline.

Scan Id (string) or Scan Id (null) (Scan Id)

Optional release-snapshot anchor. Read this surface as of ONE specific succeeded scan instead of the project's current state. Must belong to this project and be succeeded, else 404. Takes precedence over release.

Release (string) or Release (null) (Release)

Optional version anchor — read this surface as of the release carrying this label (e.g. '4.0'). Equivalent to looking the label up on /releases?release= and pinning the scan_id it returns, but as one permanent URL. Matched exactly, whitespace trimmed. Unknown label → 404, and so does a label in a project you cannot read. Ignored when scan_id is also given.

Responses

Response samples

Content type
null

Everything this project has recorded about its obligations

Authorizations:
bearerAuth
path Parameters
project_id
required
string <uuid> (Project Id)

Responses

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "total": 0
}

Per-(license, kind) obligation rows + distribution for the project's latest scan

Authorizations:
bearerAuth
path Parameters
project_id
required
string <uuid> (Project Id)
query Parameters
limit
integer (Limit) [ 1 .. 500 ]
Default: 50
offset
integer (Offset) >= 0
Default: 0
Array of Kind (strings) or Kind (null) (Kind)
Array of Category (strings) or Category (null) (Category)
Search (string) or Search (null) (Search)
sort
string (Sort) ^(category|license_name|kind|affected_count)$...
Default: "category"
order
string (Order) ^(asc|desc)$
Default: "desc"
Scan Id (string) or Scan Id (null) (Scan Id)

Optional release-snapshot anchor. Read this surface as of ONE specific succeeded scan instead of the project's current state. Must belong to this project and be succeeded, else 404. Takes precedence over release.

Release (string) or Release (null) (Release)

Optional version anchor — read this surface as of the release carrying this label (e.g. '4.0'). Equivalent to looking the label up on /releases?release= and pinning the scan_id it returns, but as one permanent URL. Matched exactly, whitespace trimmed. Unknown label → 404, and so does a label in a project you cannot read. Ignored when scan_id is also given.

Responses

Response samples

Content type
application/json
{
  • "distribution": {
    },
  • "items": [
    ],
  • "total": 0
}

Obligation drawer payload (404 if invisible to caller within this project)

Authorizations:
bearerAuth
path Parameters
project_id
required
string <uuid> (Project Id)
obligation_id
required
string <uuid> (Obligation Id)

Responses

Response samples

Content type
application/json
{
  • "affected_components": [
    ],
  • "affected_components_total": 0,
  • "affected_components_truncated": false,
  • "created_at": "2019-08-24T14:15:22Z",
  • "fulfilment": {
    },
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "kind": "string",
  • "license_category": "allowed",
  • "license_id": "38cf2dcf-77be-45c4-b03d-412af2743263",
  • "license_name": "string",
  • "license_reference_url": "string",
  • "license_spdx_id": "string",
  • "link": "string",
  • "text": "string",
  • "text_ko": "string",
  • "text_truncated": false,
  • "updated_at": "2019-08-24T14:15:22Z"
}

Remove the record, putting the obligation back to nothing recorded

Authorizations:
bearerAuth
path Parameters
project_id
required
string <uuid> (Project Id)
obligation_id
required
string <uuid> (Obligation Id)

Responses

Response samples

Content type
application/problem+json
{
  • "detail": "string",
  • "instance": "string",
  • "status": 0,
  • "title": "string",
  • "type": "about:blank"
}

Record what has been done about an obligation

Create or replace the record. One call either way.

The caller is saying what the state is now; making them find out first whether a row exists would be a round trip that answers a question about our schema rather than about their work.

Authorizations:
bearerAuth
path Parameters
project_id
required
string <uuid> (Project Id)
obligation_id
required
string <uuid> (Obligation Id)
Request Body schema: application/json
required
Assignee User Id (string) or Assignee User Id (null) (Assignee User Id)

Who is doing it. Must be an active person on the project's team.

Due On (string) or Due On (null) (Due On)

When it is needed by.

Evidence Note (string) or Evidence Note (null) (Evidence Note)

What was done, in the words of whoever did it.

Evidence Url (string) or Evidence Url (null) (Evidence Url)

Where to look: a release page, a file in the repository, a ticket. A link rather than an upload, so the portal does not become a second place the evidence can be wrong.

status
required
string (Status)

One of: not_started, in_progress, done, not_applicable. 'not_applicable' is a real answer rather than a way of hiding one: an obligation that binds a shipped binary need not bind an internal service, and saying so is more useful than an untouched row, which reads as nobody having looked.

Responses

Request samples

Content type
application/json
{
  • "assignee_user_id": "e942d24d-8cee-4fa2-bf68-164509e32e2f",
  • "due_on": "2019-08-24",
  • "evidence_note": "string",
  • "evidence_url": "string",
  • "status": "string"
}

Response samples

Content type
application/json
{
  • "assignee_user_id": "e942d24d-8cee-4fa2-bf68-164509e32e2f",
  • "completed_at": "2019-08-24T14:15:22Z",
  • "completed_by_user_id": "46d01d21-f637-42aa-839a-6d4daf42c3a4",
  • "created_at": "2019-08-24T14:15:22Z",
  • "due_on": "2019-08-24",
  • "evidence_note": "string",
  • "evidence_url": "string",
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "obligation_id": "0769bb4d-4595-4f74-b251-51ff3f5695a5",
  • "project_id": "405d8375-3514-403b-8c43-83ae74cfe0e9",
  • "status": "string",
  • "updated_at": "2019-08-24T14:15:22Z",
  • "version": 0
}

remediation

Preview the npm dependency-bump edit for a project (dry-run, no PR)

Authorizations:
bearerAuth
path Parameters
project_id
required
string <uuid> (Project Id)
Request Body schema: application/json
Any of
Manifest (string) or Manifest (null) (Manifest)

Raw package.json text to edit. When omitted, the endpoint reads the manifest from the latest preserved scan source (best-effort).

Any of
string (Manifest)

Raw package.json text to edit. When omitted, the endpoint reads the manifest from the latest preserved scan source (best-effort).

Responses

Request samples

Content type
application/json
Example
{
  • "manifest": "{\n \"name\": \"demo\",\n \"dependencies\": {\n \"lodash\": \"^4.17.20\"\n }\n}\n"
}

Response samples

Content type
application/json
{
  • "changed": true,
  • "changes": [
    ],
  • "ecosystem": "npm",
  • "edited_manifest": "{\n \"name\": \"demo\",\n \"dependencies\": {\n \"lodash\": \"^4.17.21\"\n }\n}\n",
  • "manifest_found": true,
  • "manifest_source": "preserved_source",
  • "notes": [ ],
  • "project_id": "5b8f1c2e-0c2a-4a1e-9c3d-9c2b1a0f7e11",
  • "recommendations": [
    ],
  • "scan_id": "7a1d2c3b-4e5f-6a7b-8c9d-0e1f2a3b4c5d",
  • "warnings": [
    ]
}

Open an automated npm remediation PR on the project's opted-in repo

Open (or return the existing) automated npm remediation PR.

team_admin RBAC + opt-in enforcement live in the service. Returns 201 for a freshly opened PR, 200 for an idempotent hit on an existing open PR, and 204 when there is nothing to remediate.

Authorizations:
bearerAuth
path Parameters
project_id
required
string <uuid> (Project Id)
Request Body schema: application/json
Any of
Manifest (string) or Manifest (null) (Manifest)

Raw package.json text to edit. When omitted, the service reads the manifest from the latest preserved scan source (best-effort).

Any of
string (Manifest)

Raw package.json text to edit. When omitted, the service reads the manifest from the latest preserved scan source (best-effort).

Responses

Request samples

Content type
application/json
Example
{
  • "manifest": "{\n \"name\": \"demo\",\n \"dependencies\": {\n \"lodash\": \"^4.17.20\"\n }\n}\n"
}

Response samples

Content type
application/json
{
  • "base_branch": "main",
  • "created_at": "2026-05-25T12:00:00Z",
  • "ecosystem": "npm",
  • "head_branch": "trustedoss/remediation-1a2b3c4d",
  • "id": "9c2b1a0f-7e11-4a1e-9c3d-5b8f1c2e0c2a",
  • "package_changes": [
    ],
  • "pr_number": 42,
  • "project_id": "5b8f1c2e-0c2a-4a1e-9c3d-9c2b1a0f7e11",
  • "repository_full_name": "acme/widget",
  • "status": "open",
  • "updated_at": "2026-05-25T12:00:01Z"
}

List the project's automated remediation PR records

Authorizations:
bearerAuth
path Parameters
project_id
required
string <uuid> (Project Id)
query Parameters
page
integer (Page) [ 1 .. 1000000 ]
Default: 1
page_size
integer (Page Size) [ 1 .. 200 ]
Default: 50

Responses

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "total": 1
}

reports

List download / export history for the project's Reports center

Authorizations:
bearerAuth
path Parameters
project_id
required
string <uuid> (Project Id)
query Parameters
Array of Type (strings) or Type (null) (Type)

Optional filter — one or more report_type values to include. Repeat the parameter (?type=notice&type=sbom) for multi-select. Omit for all four types.

page
integer (Page) [ 1 .. 1000000 ]
Default: 1

1-based page number.

page_size
integer (Page Size) [ 1 .. 200 ]
Default: 50

Rows per page (1..200, default 50).

Scan Id (string) or Scan Id (null) (Scan Id)

Optional release-snapshot anchor. Read this surface as of ONE specific succeeded scan instead of the project's current state. Must belong to this project and be succeeded, else 404. Takes precedence over release.

Release (string) or Release (null) (Release)

Optional version anchor — read this surface as of the release carrying this label (e.g. '4.0'). Equivalent to looking the label up on /releases?release= and pinning the scan_id it returns, but as one permanent URL. Matched exactly, whitespace trimmed. Unknown label → 404, and so does a label in a project you cannot read. Ignored when scan_id is also given.

Responses

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "page": 1,
  • "page_size": 1,
  • "total": 0
}

Download a vulnerability PDF report for the project's latest scan

Authorizations:
bearerAuth
path Parameters
project_id
required
string <uuid> (Project Id)
query Parameters
Array of Vulnerability Columns (strings) or Vulnerability Columns (null) (Vulnerability Columns)

Subset of ['cve', 'cvss', 'summary', 'status'] to render. Overrides the organization's report formatting default for this request only.

Array of Component Columns (strings) or Component Columns (null) (Component Columns)

Subset of ['name', 'version', 'license', 'severity', 'vulns'] to render. Overrides the organization's report formatting default for this request only.

Responses

Response samples

Content type
application/problem+json
{
  • "detail": "string",
  • "instance": "string",
  • "status": 0,
  • "title": "string",
  • "type": "about:blank"
}

Download a vulnerability Excel (.xlsx) report for the project's latest scan

Authorizations:
bearerAuth
path Parameters
project_id
required
string <uuid> (Project Id)

Responses

Response samples

Content type
application/problem+json
{
  • "detail": "string",
  • "instance": "string",
  • "status": 0,
  • "title": "string",
  • "type": "about:blank"
}

sbom

Export SBOM for the project's latest succeeded scan

Authorizations:
bearerAuth
path Parameters
project_id
required
string <uuid> (Project Id)
query Parameters
format
string (Format)
Default: "cyclonedx-json"
Enum: "cyclonedx-json" "cyclonedx-xml" "spdx-json" "spdx-tv"

SBOM output format.

Profile (string) or Profile (null) (Profile)

Optional policy profile (C3). Applies the project's effective license policy: 'policy-annotated' flags each violating component in place (CycloneDX properties / SPDX annotations); 'policy-filtered' drops forbidden components (and their VEX entries), recording the excluded count on the document. Omit for the canonical, cosign-signable default export. Profile exports are NOT signed.

Scan Id (string) or Scan Id (null) (Scan Id)

Optional release-snapshot anchor. Read this surface as of ONE specific succeeded scan instead of the project's current state. Must belong to this project and be succeeded, else 404. Takes precedence over release.

Release (string) or Release (null) (Release)

Optional version anchor — read this surface as of the release carrying this label (e.g. '4.0'). Equivalent to looking the label up on /releases?release= and pinning the scan_id it returns, but as one permanent URL. Matched exactly, whitespace trimmed. Unknown label → 404, and so does a label in a project you cannot read. Ignored when scan_id is also given.

Responses

Response samples

Content type
application/problem+json
{
  • "detail": "string",
  • "instance": "string",
  • "status": 0,
  • "title": "string",
  • "type": "about:blank"
}

Ingest an external CycloneDX or SPDX SBOM (queues a Celery task; returns 202)

Authorizations:
bearerAuth
path Parameters
project_id
required
string <uuid> (Project Id)
Request Body schema: multipart/form-data
required
Ref (string) or Ref (null) (Ref)

Optional git ref this SBOM was produced from (e.g. refs/heads/main, a tag, or a bare branch name). Normalized into a retention key.

Release (string) or Release (null) (Release)

Optional release/version label for the resulting snapshot.

sbom
required
string <application/octet-stream> (Sbom)

A CycloneDX-JSON (.json / .cdx.json) or SPDX (.spdx / .spdx.json / .tag) SBOM document. Trivy auto-detects the format for CVE matching; SPDX is mapped to CycloneDX for component persistence.

Responses

Response samples

Content type
application/json
{
  • "celery_task_id": "string",
  • "completed_at": "2019-08-24T14:15:22Z",
  • "created_at": "2019-08-24T14:15:22Z",
  • "current_step": "string",
  • "error_message": "string",
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "kind": "source",
  • "metadata": { },
  • "progress_percent": 0,
  • "project_id": "405d8375-3514-403b-8c43-83ae74cfe0e9",
  • "project_name": "string",
  • "project_slug": "string",
  • "ref": "string",
  • "release": "string",
  • "requested_by_user_id": "ed1d5170-d6e9-4e60-8754-78ea9640e9db",
  • "started_at": "2019-08-24T14:15:22Z",
  • "status": "queued",
  • "superseded_at": "2019-08-24T14:15:22Z",
  • "updated_at": "2019-08-24T14:15:22Z"
}

Download the in-toto / SLSA provenance attestation for the latest SBOM

Authorizations:
bearerAuth
path Parameters
project_id
required
string <uuid> (Project Id)

Responses

Response samples

Content type
application/problem+json
{
  • "detail": "string",
  • "instance": "string",
  • "status": 0,
  • "title": "string",
  • "type": "about:blank"
}

Download the Fulcio certificate for the attestation (keyless signing only)

Authorizations:
bearerAuth
path Parameters
project_id
required
string <uuid> (Project Id)

Responses

Response samples

Content type
application/problem+json
{
  • "detail": "string",
  • "instance": "string",
  • "status": 0,
  • "title": "string",
  • "type": "about:blank"
}

Download the Fulcio signing certificate (keyless signing only)

Authorizations:
bearerAuth
path Parameters
project_id
required
string <uuid> (Project Id)

Responses

Response samples

Content type
application/problem+json
{
  • "detail": "string",
  • "instance": "string",
  • "status": 0,
  • "title": "string",
  • "type": "about:blank"
}

Download the cosign public key for verifying SBOM signatures

Authorizations:
bearerAuth
path Parameters
project_id
required
string <uuid> (Project Id)

Responses

Response samples

Content type
application/problem+json
{
  • "detail": "string",
  • "instance": "string",
  • "status": 0,
  • "title": "string",
  • "type": "about:blank"
}

Download the detached cosign signature for the latest SBOM

Authorizations:
bearerAuth
path Parameters
project_id
required
string <uuid> (Project Id)

Responses

Response samples

Content type
application/problem+json
{
  • "detail": "string",
  • "instance": "string",
  • "status": 0,
  • "title": "string",
  • "type": "about:blank"
}

Download a zip bundle (SBOM + signature + cert/public-key + attestation + README)

Authorizations:
bearerAuth
path Parameters
project_id
required
string <uuid> (Project Id)

Responses

Response samples

Content type
application/problem+json
{
  • "detail": "string",
  • "instance": "string",
  • "status": 0,
  • "title": "string",
  • "type": "about:blank"
}

Get the conformance verdict for an ingested SBOM scan

Authorizations:
bearerAuth
path Parameters
project_id
required
string <uuid> (Project Id)
scan_id
required
string <uuid> (Scan Id)

Responses

Response samples

Content type
application/json
{
  • "ai_assessment": {
    },
  • "checks": [
    ],
  • "component_count": 0,
  • "created_at": "2019-08-24T14:15:22Z",
  • "hash_coverage_pct": 0,
  • "license_coverage_pct": 0,
  • "n_fail": 0,
  • "n_warn": 0,
  • "project_id": "405d8375-3514-403b-8c43-83ae74cfe0e9",
  • "purl_coverage_pct": 0,
  • "regulatory_crosswalk": {
    },
  • "result": "pass",
  • "scan_id": "9a59f0f5-5572-476d-a7fc-c960ef43a5af",
  • "source_format": "cyclonedx"
}

scans

List scans for a project (most recent first)

Authorizations:
bearerAuth
path Parameters
project_id
required
string <uuid> (Project Id)
query Parameters
page
integer (Page) [ 1 .. 1000000 ]
Default: 1
size
integer (Size) [ 1 .. 100 ]
Default: 20
Ref (string) or Ref (null) (Ref)

Optional branch filter. Accepts a bare branch (main) or a fully-qualified ref (refs/heads/main, refs/pull/12/merge), normalized the same way scan triggers normalize it. Unlike the releases list this covers every status, so it answers "what has this branch done lately" including failures.

Responses

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "page": 0,
  • "size": 0,
  • "total": 0
}

List scans across every project accessible to the caller

Authorizations:
bearerAuth
query Parameters
Status (string) or Status (null) (Status)

Filter by scan status.

page
integer (Page) [ 1 .. 1000000 ]
Default: 1
size
integer (Size) [ 1 .. 100 ]
Default: 20

Responses

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "page": 0,
  • "size": 0,
  • "total": 0
}

Delete a terminal scan and its findings (own-team scans only)

Hard-delete a terminal scan and (via cascade) its findings / components.

DT-style retention reclaims most stale scans automatically; this is the manual escape hatch. Auth: any team member (developer+). The owning-team check lives in the service (delete_scan), which existence-hides other teams' scans as 404. Active scans (queued/running) return 409 — cancel first. A release-labelled scan returns 409 unless force=true.

Authorizations:
bearerAuth
path Parameters
scan_id
required
string <uuid> (Scan Id)
query Parameters
force
boolean (Force)
Default: false

Delete even when the scan carries an explicit metadata.release label. Release-labelled snapshots are immutable by default.

Responses

Response samples

Content type
application/problem+json
{
  • "detail": "string",
  • "instance": "string",
  • "status": 0,
  • "title": "string",
  • "type": "about:blank"
}

Read one scan (IDOR-safe via project team membership)

Authorizations:
bearerAuth
path Parameters
scan_id
required
string <uuid> (Scan Id)

Responses

Response samples

Content type
application/json
{
  • "celery_task_id": "string",
  • "completed_at": "2019-08-24T14:15:22Z",
  • "created_at": "2019-08-24T14:15:22Z",
  • "current_step": "string",
  • "error_message": "string",
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "kind": "source",
  • "metadata": { },
  • "progress_percent": 0,
  • "project_id": "405d8375-3514-403b-8c43-83ae74cfe0e9",
  • "project_name": "string",
  • "project_slug": "string",
  • "ref": "string",
  • "release": "string",
  • "requested_by_user_id": "ed1d5170-d6e9-4e60-8754-78ea9640e9db",
  • "started_at": "2019-08-24T14:15:22Z",
  • "status": "queued",
  • "superseded_at": "2019-08-24T14:15:22Z",
  • "updated_at": "2019-08-24T14:15:22Z"
}

Cancel a queued / running scan owned by the caller's team

Cancel one of the caller's own team's scans.

PR-A1 (scan stability). Auth: any authenticated team member (developer or higher). The owning-team check lives in the service (cancel_scan_for_actor) which existence-hides other teams' scans as 404 — so a developer cannot probe scan ids belonging to other teams.

Admin force-cancel (POST /v1/admin/scans/{id}/cancel) remains separate and cross-team; the two share the same revoke + status-mutation core.

Authorizations:
bearerAuth
path Parameters
scan_id
required
string <uuid> (Scan Id)

Responses

Response samples

Content type
application/json
null

Download the persisted tool log for one scan

Stream the per-scan scan.log written by tasks._progress.publish_log.

Authorization: same gate as GET /v1/scans/{scan_id} — reuses services.scan_service.get_scan so team-membership / super-admin rules stay in lock-step with the metadata endpoint. A non-member sees the same 404 as a non-existent scan id (existence-hide) so a developer cannot probe scan ids belonging to other teams via this endpoint.

Lifecycle: the file is written incrementally by the worker as the scan runs. While the scan is still running the response returns whatever has been flushed so far (the publisher uses a line-buffered handle, so each completed line is on disk by the time it is on the WebSocket). After the scan terminates the file stays on disk until workspace_cleaner reaps the parent workspace directory (current default: per WORKSPACE_ORPHAN_MAX_AGE_SECONDS).

Authorizations:
bearerAuth
path Parameters
scan_id
required
string <uuid> (Scan Id)

Responses

Response samples

Content type
null

Where a scan's results came from (IDOR-safe via project team membership)

Serve the scan's recorded inputs.

Existence-hide comes from get_scan — the same resolver the scan read uses — so an id in another team is indistinguishable here from one that never existed. Nothing about this route may answer a question the scan read would refuse.

A scan with neither half recorded is a 200 with two nulls, not a 404: the scan exists and the honest answer is that nothing was recorded, which a 404 would misreport as "no such scan".

Authorizations:
bearerAuth
path Parameters
scan_id
required
string <uuid> (Scan Id)

Responses

Response samples

Content type
application/json
{
  • "document": {
    },
  • "kind": "source",
  • "manifests": {
    },
  • "scan_id": "9a59f0f5-5572-476d-a7fc-c960ef43a5af"
}

Start a scan on many projects (auth required, role >= developer)

Start the first scan on a set of freshly registered projects.

The team's concurrent-scan cap is re-counted for each row against the team's live active-scan total, so a batch starts scans up to the cap and reports the rest as rate_limited with an estimated wait. It is not a way around the cap: the cap protects the shared worker pool, and queueing past it would move the load rather than shed it. Send the remainder again once the earlier scans finish.

A project that already has a queued or running scan counts as success, for the same reason an existing project does in the project batch: that is the state the caller asked for.

Status is 201 when every row started and 207 when any did not.

Authorizations:
bearerAuth
Request Body schema: application/json
required
project_ids
required
Array of strings <uuid> (Project Ids) [ 1 .. 200 ] items [ items <uuid > ]

Projects to scan, at most 200. The team's concurrent-scan cap still applies and is re-counted per row, so a batch starts scans up to the cap and reports the rest as rate_limited rather than queueing past it.

Ref (string) or Ref (null) (Ref)

Git ref to scan, applied to every row. Omit for the default branch.

Responses

Request samples

Content type
application/json
{
  • "project_ids": [
    ],
  • "ref": "string"
}

Response samples

Content type
application/json
{
  • "all_succeeded": true,
  • "already_existed": 0,
  • "created": 0,
  • "failed": 0,
  • "failed_by_status": {
    },
  • "rows": [
    ],
  • "total": 0
}

source-tree

Read one file from a scan's preserved source + per-line license matches

Authorizations:
bearerAuth
path Parameters
project_id
required
string <uuid> (Project Id)
query Parameters
path
required
string (Path)

File to read, relative to the source root.

raw
boolean (Raw)
Default: false

When true, stream the FULL member as application/octet-stream (no per-file viewer cap) for download instead of the capped JSON preview. Same path-traversal / symlink defences apply.

Scan Id (string) or Scan Id (null) (Scan Id)

Optional release-snapshot anchor. Read this surface as of ONE specific succeeded scan instead of the project's current state. Must belong to this project and be succeeded, else 404. Takes precedence over release.

Release (string) or Release (null) (Release)

Optional version anchor — read this surface as of the release carrying this label (e.g. '4.0'). Equivalent to looking the label up on /releases?release= and pinning the scan_id it returns, but as one permanent URL. Matched exactly, whitespace trimmed. Unknown label → 404, and so does a label in a project you cannot read. Ignored when scan_id is also given.

Responses

Response samples

Content type
{
  • "byte_size": 1071,
  • "content": "MIT License\n\nCopyright (c) ...",
  • "encoding": "utf-8",
  • "license_matches": [
    ],
  • "path": "LICENSE",
  • "scan_id": "5b6c0f2e-3a1d-4e8a-9b2c-7d4e1f0a9c33",
  • "truncated": false
}

List immediate children of a directory in a scan's preserved source

Authorizations:
bearerAuth
path Parameters
project_id
required
string <uuid> (Project Id)
query Parameters
path
string (Path)
Default: ""

Directory whose immediate children to list. Empty = root.

page
integer (Page) [ 1 .. 1000000 ]
Default: 1

1-based page index.

size
integer (Size) [ 1 .. 500 ]
Default: 100

Page size (max 500).

Scan Id (string) or Scan Id (null) (Scan Id)

Optional release-snapshot anchor. Read this surface as of ONE specific succeeded scan instead of the project's current state. Must belong to this project and be succeeded, else 404. Takes precedence over release.

Release (string) or Release (null) (Release)

Optional version anchor — read this surface as of the release carrying this label (e.g. '4.0'). Equivalent to looking the label up on /releases?release= and pinning the scan_id it returns, but as one permanent URL. Matched exactly, whitespace trimmed. Unknown label → 404, and so does a label in a project you cannot read. Ignored when scan_id is also given.

Responses

Response samples

Content type
application/json
{
  • "entries": [
    ],
  • "page": 1,
  • "path": "src",
  • "scan_id": "5b6c0f2e-3a1d-4e8a-9b2c-7d4e1f0a9c33",
  • "size": 50,
  • "total": 1
}

vex

Export a VEX document from the project's current finding triage

Authorizations:
bearerAuth
path Parameters
project_id
required
string <uuid> (Project Id)
query Parameters
format
string (Format)
Default: "openvex"
Enum: "openvex" "cyclonedx"

VEX output format.

Responses

Response samples

Content type
application/problem+json
{
  • "detail": "string",
  • "instance": "string",
  • "status": 0,
  • "title": "string",
  • "type": "about:blank"
}

Import a VEX document, auto-transitioning matching findings (team_admin)

Authorizations:
bearerAuth
path Parameters
project_id
required
string <uuid> (Project Id)
Request Body schema: multipart/form-data
required
upload
required
string <application/octet-stream> (Upload)

An OpenVEX or CycloneDX VEX JSON document (format auto-detected).

Responses

Response samples

Content type
application/json
{
  • "applied": 0,
  • "errors": [
    ],
  • "format": "openvex",
  • "matched": 0,
  • "skipped": 0
}

vulnerabilities

Paginated CVE findings for the project's latest scan

Authorizations:
bearerAuth
path Parameters
project_id
required
string <uuid> (Project Id)
query Parameters
limit
integer (Limit) [ 1 .. 500 ]
Default: 50
offset
integer (Offset) >= 0
Default: 0
Search (string) or Search (null) (Search)
Array of Severity (strings) or Severity (null) (Severity)
Array of Status (strings) or Status (null) (Status)
Array of License Category (strings) or License Category (null) (License Category)

W2 #33 — License risk-axis filter. Repeatable; accepted values: forbidden, conditional, allowed, unknown (the cv had no license finding in this scan). Unknown values are dropped, so a query that filters ONLY by unknown values returns an empty page (not a 422). Omit to include all categories.

Min Epss (number) or Min Epss (null) (Min Epss)

Keep only findings whose CVE has an EPSS exploit-probability >= this threshold, in [0, 1]. CVEs with no published EPSS score are excluded. Omit to disable EPSS filtering.

Reachable (string) or Reachable (null) (Reachable)

Tri-state reachability filter (v2.3). true → only findings whose vulnerable symbol is reachable on the call graph; false → only findings an analyser proved NOT reachable; unknown → only not-analysed findings (reachable IS NULL). Omit to disable the reachability filter.

Sla (string) or Sla (null) (Sla)

X1 SLA filter (single value). overdue → past the per-severity SLA due date; imminent → due within 7 days; ok → inside the window. Findings with no SLA (info / unknown severity) never match any token. Omit to disable SLA filtering.

Assignee (string) or Assignee (null) (Assignee)

Ownership filter. me returns the caller's own findings, unassigned the ones nobody has taken, and inactive the ones assigned to somebody whose account is closed, which look owned and cannot move. Values name a state rather than a person: the parameter already says whose. Deliberately not a user id, which would add a way to ask which findings a named person owns without any screen needing it.

sort
string (Sort) ^(severity|cvss|status|discovered_at|epss|rea...
Default: "severity"

Sort key. reachable ranks reachable findings first (then not-analysed, then proven-unreachable), tie-broken by severity desc. component sorts by affected package name. priority is the triage ranking: CISA-KEV-listed CVEs first, then severity desc, then EPSS desc (nulls last). sla_due orders by the SLA due date (asc = most urgent first); findings with no SLA sort last in both directions.

order
string (Order) ^(asc|desc)$
Default: "desc"
Scan Id (string) or Scan Id (null) (Scan Id)

Optional release-snapshot anchor. Read this surface as of ONE specific succeeded scan instead of the project's current state. Must belong to this project and be succeeded, else 404. Takes precedence over release.

Release (string) or Release (null) (Release)

Optional version anchor — read this surface as of the release carrying this label (e.g. '4.0'). Equivalent to looking the label up on /releases?release= and pinning the scan_id it returns, but as one permanent URL. Matched exactly, whitespace trimmed. Unknown label → 404, and so does a label in a project you cannot read. Ignored when scan_id is also given.

Responses

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "limit": 0,
  • "offset": 0,
  • "severity_distribution": {
    },
  • "total": 0
}

The filtered CVE findings as CSV

The same rows the list endpoint would return, without the paging.

Every filter the list accepts is accepted here and applied by the same code: the export pages the list service rather than rebuilding its query, so a filter that narrows the screen narrows the file identically, and the cross-team check the list performs is the one this performs.

limit and offset are deliberately absent. Exporting "page 3 of what I am looking at" is not a thing anyone wants, and accepting them would invite a caller to walk the table with a script.

sort/order are still accepted and still validated (an invalid value still 422s), but no longer decide the exported rows' order (#463): the export walks a fixed key (the finding id) instead of the screen's 8 sort modes, so a filtered export that reaches real depth does not pay for OFFSET's cost growing with it. Kept on the signature rather than dropped so an existing caller's ?sort=...&order=... keeps 200ing instead of 422ing on an unknown parameter.

Authorizations:
bearerAuth
path Parameters
project_id
required
string <uuid> (Project Id)
query Parameters
Search (string) or Search (null) (Search)
Array of Severity (strings) or Severity (null) (Severity)
Array of Status (strings) or Status (null) (Status)
Array of License Category (strings) or License Category (null) (License Category)
Min Epss (number) or Min Epss (null) (Min Epss)
Reachable (string) or Reachable (null) (Reachable)
Sla (string) or Sla (null) (Sla)
Assignee (string) or Assignee (null) (Assignee)
sort
string (Sort) ^(severity|cvss|status|discovered_at|epss|rea...
Default: "severity"
order
string (Order) ^(asc|desc)$
Default: "desc"
Scan Id (string) or Scan Id (null) (Scan Id)

Optional release-snapshot anchor. Read this surface as of ONE specific succeeded scan instead of the project's current state. Must belong to this project and be succeeded, else 404. Takes precedence over release.

Release (string) or Release (null) (Release)

Optional version anchor — read this surface as of the release carrying this label (e.g. '4.0'). Equivalent to looking the label up on /releases?release= and pinning the scan_id it returns, but as one permanent URL. Matched exactly, whitespace trimmed. Unknown label → 404, and so does a label in a project you cannot read. Ignored when scan_id is also given.

Responses

Response samples

Content type
application/problem+json
{
  • "detail": "string",
  • "instance": "string",
  • "status": 0,
  • "title": "string",
  • "type": "about:blank"
}

Open findings grouped by the component upgrade that fixes them

The "Group by upgrade" view: the resolved scan's OPEN findings grouped by the minimum safe upgrade that clears them, most-actionable first.

Same auth / snapshot semantics as the list endpoint — non-member → 403, missing project → 404, unresolvable ?scan_id= → 404, no succeeded scan → 200 with an empty clusters and total_findings == 0.

Authorizations:
bearerAuth
path Parameters
project_id
required
string <uuid> (Project Id)
query Parameters
limit
integer (Limit) [ 1 .. 200 ]
Default: 50
Scan Id (string) or Scan Id (null) (Scan Id)

Optional release-snapshot anchor. Read this surface as of ONE specific succeeded scan instead of the project's current state. Must belong to this project and be succeeded, else 404. Takes precedence over release.

Release (string) or Release (null) (Release)

Optional version anchor — read this surface as of the release carrying this label (e.g. '4.0'). Equivalent to looking the label up on /releases?release= and pinning the scan_id it returns, but as one permanent URL. Matched exactly, whitespace trimmed. Unknown label → 404, and so does a label in a project you cannot read. Ignored when scan_id is also given.

Responses

Response samples

Content type
application/json
{
  • "clusters": [
    ],
  • "scan_id": "9a59f0f5-5572-476d-a7fc-c960ef43a5af",
  • "total_clusters": 0,
  • "total_findings": 0,
  • "truncated": false
}

Transition many findings in one project to the same VEX status

W2 #33b — apply one VEX transition across many findings in one round-trip.

Per-row failures (404 / 403 / 422) are surfaced in the response envelope so the UI can render "succeeded N · failed M" with per-row details. Only envelope-level shape violations (empty list, > cap, unknown enum) return RFC 7807 — those would still abort a per-row partial commit, so they belong on the envelope rather than masquerading as per-row outcomes.

Authorizations:
bearerAuth
path Parameters
project_id
required
string <uuid> (Project Id)
Request Body schema: application/json
required
finding_ids
required
Array of strings <uuid> (Finding Ids) [ 1 .. 200 ] items [ items <uuid > ]

Finding ids to transition. 1..200 entries; duplicates are deduplicated server-side. Ids that do not exist in THIS project (cross-project / cross-team / never-existed) are reported as a per-row 404 — they do not abort the bulk.

Justification (string) or Justification (null) (Justification)

Free-form note recorded as analysis_justification on every successfully-transitioned row. Omit to leave each row's existing justification untouched.

target_status
required
string (Target Status)
Enum: "new" "analyzing" "exploitable" "not_affected" "false_positive" "suppressed" "fixed"

Target status applied to every supplied id. Per-row transition-matrix and role checks still run (developer → suppressed is blocked per row even in a bulk that contains it).

Responses

Request samples

Content type
application/json
{
  • "finding_ids": [
    ],
  • "justification": "string",
  • "target_status": "new"
}

Response samples

Content type
application/json
{
  • "failed": 0,
  • "results": [
    ],
  • "succeeded": 0,
  • "target_status": "new",
  • "total": 1
}

Vulnerability finding drawer payload (404 if invisible to caller)

Authorizations:
bearerAuth
path Parameters
finding_id
required
string <uuid> (Finding Id)

Responses

Response samples

Content type
application/json
{
  • "affected_components": [
    ],
  • "analysis_justification": "string",
  • "analysis_source": "manual",
  • "analysis_state": "string",
  • "analyst_user_id": "0983c53a-1216-4076-815a-f64301f7aa2c",
  • "analyzed_at": "2019-08-24T14:15:22Z",
  • "assignee_is_active": true,
  • "assignee_user_id": "e942d24d-8cee-4fa2-bf68-164509e32e2f",
  • "created_at": "2019-08-24T14:15:22Z",
  • "cve_id": "string",
  • "cvss_score": 0,
  • "cvss_vector": "string",
  • "details": "string",
  • "due_on": "2019-08-24",
  • "due_source": "sla",
  • "effective_due_date": "2019-08-24T14:15:22Z",
  • "epss_percentile": 0.99412,
  • "epss_score": 0.97123,
  • "first_detected_at": "2019-08-24T14:15:22Z",
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "kev": false,
  • "kev_due_date": "2026-07-22",
  • "manual_due_ignored": false,
  • "matching_provenance": {
    },
  • "project_id": "405d8375-3514-403b-8c43-83ae74cfe0e9",
  • "published_at": "2019-08-24T14:15:22Z",
  • "reachability_analyzed_at": "2019-08-24T14:15:22Z",
  • "reachability_source": "govulncheck",
  • "reachable": true,
  • "references": [
    ],
  • "scan_id": "9a59f0f5-5572-476d-a7fc-c960ef43a5af",
  • "severity": "critical",
  • "sla_due_date": "2019-08-24T14:15:22Z",
  • "sla_status": "ok",
  • "status": "new",
  • "status_history": [
    ],
  • "summary": "string",
  • "ticket_check_error": "string",
  • "ticket_checked_at": "2019-08-24T14:15:22Z",
  • "ticket_key": "string",
  • "ticket_resolved": true,
  • "ticket_status": "string",
  • "ticket_url": "string",
  • "updated_at": "2019-08-24T14:15:22Z",
  • "upgrade_recommendation": {
    },
  • "vex_origin": {
    }
}

Set a finding's owner, deadline and ticket (audit-logged)

Authorizations:
bearerAuth
path Parameters
finding_id
required
string <uuid> (Finding Id)
Request Body schema: application/json
required
Assignee User Id (string) or Assignee User Id (null) (Assignee User Id)

Who owns remediating this finding. Must be an active, non-service account on the project's team, the same rule obligations use. Send null to unassign.

Due On (string) or Due On (null) (Due On)

Remediation deadline as a calendar date, interpreted in UTC. The EARLIER of this and the per-severity SLA governs, so a date after the policy's is stored but does not move the deadline; the response's manual_due_ignored says when that happened. Send null to clear.

If Match (string) or If Match (null) (If Match)

Optional optimistic-concurrency token, same contract as the status PATCH: the server compares it against the row's current updated_at and returns 409 on mismatch.

Ticket Key (string) or Ticket Key (null) (Ticket Key)

Ticket identifier, e.g. SEC-1234. Send null to clear.

Ticket Url (string) or Ticket Url (null) (Ticket Url)

Where the work is tracked externally. Send null to clear.

Responses

Request samples

Content type
application/json
{
  • "assignee_user_id": "e942d24d-8cee-4fa2-bf68-164509e32e2f",
  • "due_on": "2019-08-24",
  • "if_match": "2019-08-24T14:15:22Z",
  • "ticket_key": "string",
  • "ticket_url": "string"
}

Response samples

Content type
application/json
{
  • "affected_components": [
    ],
  • "analysis_justification": "string",
  • "analysis_source": "manual",
  • "analysis_state": "string",
  • "analyst_user_id": "0983c53a-1216-4076-815a-f64301f7aa2c",
  • "analyzed_at": "2019-08-24T14:15:22Z",
  • "assignee_is_active": true,
  • "assignee_user_id": "e942d24d-8cee-4fa2-bf68-164509e32e2f",
  • "created_at": "2019-08-24T14:15:22Z",
  • "cve_id": "string",
  • "cvss_score": 0,
  • "cvss_vector": "string",
  • "details": "string",
  • "due_on": "2019-08-24",
  • "due_source": "sla",
  • "effective_due_date": "2019-08-24T14:15:22Z",
  • "epss_percentile": 0.99412,
  • "epss_score": 0.97123,
  • "first_detected_at": "2019-08-24T14:15:22Z",
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "kev": false,
  • "kev_due_date": "2026-07-22",
  • "manual_due_ignored": false,
  • "matching_provenance": {
    },
  • "project_id": "405d8375-3514-403b-8c43-83ae74cfe0e9",
  • "published_at": "2019-08-24T14:15:22Z",
  • "reachability_analyzed_at": "2019-08-24T14:15:22Z",
  • "reachability_source": "govulncheck",
  • "reachable": true,
  • "references": [
    ],
  • "scan_id": "9a59f0f5-5572-476d-a7fc-c960ef43a5af",
  • "severity": "critical",
  • "sla_due_date": "2019-08-24T14:15:22Z",
  • "sla_status": "ok",
  • "status": "new",
  • "status_history": [
    ],
  • "summary": "string",
  • "ticket_check_error": "string",
  • "ticket_checked_at": "2019-08-24T14:15:22Z",
  • "ticket_key": "string",
  • "ticket_resolved": true,
  • "ticket_status": "string",
  • "ticket_url": "string",
  • "updated_at": "2019-08-24T14:15:22Z",
  • "upgrade_recommendation": {
    },
  • "vex_origin": {
    }
}

Transition a vulnerability finding's VEX status (audit-logged)

Authorizations:
bearerAuth
path Parameters
finding_id
required
string <uuid> (Finding Id)
Request Body schema: application/json
required
If Match (string) or If Match (null) (If Match)

Optional optimistic-concurrency token. When supplied, the server compares this against the current row's updated_at. Mismatch → 409 Conflict (RFC 7807). When omitted, the update proceeds without lock (best-effort).

Justification (string) or Justification (null) (Justification)

Free-form note recorded as analysis_justification. Required by the UI for VEX-significant transitions but enforced softly: the API accepts an empty justification (regulators often re-trigger the transition once with the note attached).

status
required
string (Status)
Enum: "new" "analyzing" "exploitable" "not_affected" "false_positive" "suppressed" "fixed"

Target status.

Responses

Request samples

Content type
application/json
{
  • "if_match": "2019-08-24T14:15:22Z",
  • "justification": "string",
  • "status": "new"
}

Response samples

Content type
application/json
{
  • "affected_components": [
    ],
  • "analysis_justification": "string",
  • "analysis_source": "manual",
  • "analysis_state": "string",
  • "analyst_user_id": "0983c53a-1216-4076-815a-f64301f7aa2c",
  • "analyzed_at": "2019-08-24T14:15:22Z",
  • "assignee_is_active": true,
  • "assignee_user_id": "e942d24d-8cee-4fa2-bf68-164509e32e2f",
  • "created_at": "2019-08-24T14:15:22Z",
  • "cve_id": "string",
  • "cvss_score": 0,
  • "cvss_vector": "string",
  • "details": "string",
  • "due_on": "2019-08-24",
  • "due_source": "sla",
  • "effective_due_date": "2019-08-24T14:15:22Z",
  • "epss_percentile": 0.99412,
  • "epss_score": 0.97123,
  • "first_detected_at": "2019-08-24T14:15:22Z",
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "kev": false,
  • "kev_due_date": "2026-07-22",
  • "manual_due_ignored": false,
  • "matching_provenance": {
    },
  • "project_id": "405d8375-3514-403b-8c43-83ae74cfe0e9",
  • "published_at": "2019-08-24T14:15:22Z",
  • "reachability_analyzed_at": "2019-08-24T14:15:22Z",
  • "reachability_source": "govulncheck",
  • "reachable": true,
  • "references": [
    ],
  • "scan_id": "9a59f0f5-5572-476d-a7fc-c960ef43a5af",
  • "severity": "critical",
  • "sla_due_date": "2019-08-24T14:15:22Z",
  • "sla_status": "ok",
  • "status": "new",
  • "status_history": [
    ],
  • "summary": "string",
  • "ticket_check_error": "string",
  • "ticket_checked_at": "2019-08-24T14:15:22Z",
  • "ticket_key": "string",
  • "ticket_resolved": true,
  • "ticket_status": "string",
  • "ticket_url": "string",
  • "updated_at": "2019-08-24T14:15:22Z",
  • "upgrade_recommendation": {
    },
  • "vex_origin": {
    }
}

Read the finding's external ticket back and record its state

Authorizations:
bearerAuth
path Parameters
finding_id
required
string <uuid> (Finding Id)

Responses

Response samples

Content type
application/json
{
  • "finding_id": "feebf65a-2eaa-4fae-aab2-772450efdffe",
  • "ticket_check_error": "string",
  • "ticket_checked_at": "2019-08-24T14:15:22Z",
  • "ticket_resolved": true,
  • "ticket_status": "string"
}

report-format-templates

Remove the organization's report formatting defaults

Authorizations:
bearerAuth
path Parameters
organization_id
required
string <uuid> (Organization Id)

Responses

Response samples

Content type
application/problem+json
{
  • "detail": "string",
  • "instance": "string",
  • "status": 0,
  • "title": "string",
  • "type": "about:blank"
}

Read the organization's report formatting defaults

Authorizations:
bearerAuth
path Parameters
organization_id
required
string <uuid> (Organization Id)

Responses

Response samples

Content type
application/json
{
  • "component_columns": [
    ],
  • "created_at": "2019-08-24T14:15:22Z",
  • "header_text": "string",
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "org_label": "string",
  • "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6",
  • "updated_at": "2019-08-24T14:15:22Z",
  • "vulnerability_columns": [
    ]
}

Create or replace the organization's report formatting defaults

Authorizations:
bearerAuth
path Parameters
organization_id
required
string <uuid> (Organization Id)
Request Body schema: application/json
required
Array of Component Columns (strings) or Component Columns (null) (Component Columns)

Non-empty subset of ['name', 'version', 'license', 'severity', 'vulns']. Null means every column renders (current behavior).

Header Text (string) or Header Text (null) (Header Text)

Plain text printed under the report header. Null clears it.

Org Label (string) or Org Label (null) (Org Label)

Replaces the default brand text in the report header. Null clears it.

Array of Vulnerability Columns (strings) or Vulnerability Columns (null) (Vulnerability Columns)

Non-empty subset of ['cve', 'cvss', 'summary', 'status'], in canonical order regardless of the order given. Null means every column renders (current behavior).

Responses

Request samples

Content type
application/json
{
  • "component_columns": [
    ],
  • "header_text": "string",
  • "org_label": "string",
  • "vulnerability_columns": [
    ]
}

Response samples

Content type
application/json
{
  • "component_columns": [
    ],
  • "created_at": "2019-08-24T14:15:22Z",
  • "header_text": "string",
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "org_label": "string",
  • "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6",
  • "updated_at": "2019-08-24T14:15:22Z",
  • "vulnerability_columns": [
    ]
}

saved-searches

List the caller's saved searches

Authorizations:
bearerAuth

Responses

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "limit": 20,
  • "total": 0
}

Save a search

Authorizations:
bearerAuth
Request Body schema: application/json
required
kind
required
string (Kind)

Which search tab the params belong to: projects, components, vulnerabilities, or licenses.

name
required
string (Name) [ 1 .. 60 ] characters
object (Params)

The saved query string, replayed verbatim on open. Opaque to the server — it is whatever filters the page carried at save time.

Responses

Request samples

Content type
application/json
{
  • "kind": "string",
  • "name": "string",
  • "params": { }
}

Response samples

Content type
application/json
{
  • "created_at": "2019-08-24T14:15:22Z",
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "kind": "string",
  • "name": "string",
  • "params": { }
}

Delete one of the caller's saved searches

Authorizations:
bearerAuth
path Parameters
saved_search_id
required
string <uuid> (Saved Search Id)

Responses

Response samples

Content type
application/problem+json
{
  • "detail": "string",
  • "instance": "string",
  • "status": 0,
  • "title": "string",
  • "type": "about:blank"
}

scan-schedules

What will actually scan this project on a schedule, if anything

Authorizations:
bearerAuth
path Parameters
project_id
required
string <uuid> (Project Id)

Responses

Response samples

Content type
application/json
{
  • "cadence": "string",
  • "day_of_week": 0,
  • "hour": 0,
  • "is_active": true,
  • "project_id": "405d8375-3514-403b-8c43-83ae74cfe0e9",
  • "source": "string",
  • "timezone": "string"
}

Create or replace the organization default

Authorizations:
bearerAuth
path Parameters
organization_id
required
string <uuid> (Organization Id)
Request Body schema: application/json
required
Cadence (string) or Cadence (null) (Cadence)

'daily' or 'weekly'. Null means this row decides nothing yet.

Day Of Week (integer) or Day Of Week (null) (Day Of Week)

0=Monday..6=Sunday. Required for 'weekly', forbidden for 'daily'.

Hour (integer) or Hour (null) (Hour)

Local hour-of-day (0-23) the schedule fires, read in timezone.

is_active
boolean (Is Active)
Default: true

Whether this row's schedule fires. False opts a project out of an organization default without deleting the row.

timezone
string (Timezone)
Default: "UTC"

IANA zone name the hour/day-of-week are read against.

Responses

Request samples

Content type
application/json
{
  • "cadence": "string",
  • "day_of_week": 6,
  • "hour": 23,
  • "is_active": true,
  • "timezone": "UTC"
}

Response samples

Content type
application/json
{
  • "cadence": "string",
  • "created_at": "2019-08-24T14:15:22Z",
  • "day_of_week": 0,
  • "hour": 0,
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "is_active": true,
  • "last_triggered_at": "2019-08-24T14:15:22Z",
  • "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6",
  • "project_id": "405d8375-3514-403b-8c43-83ae74cfe0e9",
  • "timezone": "string",
  • "updated_at": "2019-08-24T14:15:22Z"
}

Drop a project's schedule so it follows its organization default again

Authorizations:
bearerAuth
path Parameters
project_id
required
string <uuid> (Project Id)

Responses

Response samples

Content type
application/problem+json
{
  • "detail": "string",
  • "instance": "string",
  • "status": 0,
  • "title": "string",
  • "type": "about:blank"
}

Read a project's own schedule row

Authorizations:
bearerAuth
path Parameters
project_id
required
string <uuid> (Project Id)

Responses

Response samples

Content type
application/json
{
  • "cadence": "string",
  • "created_at": "2019-08-24T14:15:22Z",
  • "day_of_week": 0,
  • "hour": 0,
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "is_active": true,
  • "last_triggered_at": "2019-08-24T14:15:22Z",
  • "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6",
  • "project_id": "405d8375-3514-403b-8c43-83ae74cfe0e9",
  • "timezone": "string",
  • "updated_at": "2019-08-24T14:15:22Z"
}

Create or replace a project's own scan schedule

Authorizations:
bearerAuth
path Parameters
project_id
required
string <uuid> (Project Id)
Request Body schema: application/json
required
Cadence (string) or Cadence (null) (Cadence)

'daily' or 'weekly'. Null means this row decides nothing yet.

Day Of Week (integer) or Day Of Week (null) (Day Of Week)

0=Monday..6=Sunday. Required for 'weekly', forbidden for 'daily'.

Hour (integer) or Hour (null) (Hour)

Local hour-of-day (0-23) the schedule fires, read in timezone.

is_active
boolean (Is Active)
Default: true

Whether this row's schedule fires. False opts a project out of an organization default without deleting the row.

timezone
string (Timezone)
Default: "UTC"

IANA zone name the hour/day-of-week are read against.

Responses

Request samples

Content type
application/json
{
  • "cadence": "string",
  • "day_of_week": 6,
  • "hour": 23,
  • "is_active": true,
  • "timezone": "UTC"
}

Response samples

Content type
application/json
{
  • "cadence": "string",
  • "created_at": "2019-08-24T14:15:22Z",
  • "day_of_week": 0,
  • "hour": 0,
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "is_active": true,
  • "last_triggered_at": "2019-08-24T14:15:22Z",
  • "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6",
  • "project_id": "405d8375-3514-403b-8c43-83ae74cfe0e9",
  • "timezone": "string",
  • "updated_at": "2019-08-24T14:15:22Z"
}

search

Cross-project global search (components + vulnerabilities)

Authorizations:
bearerAuth
query Parameters
q
required
string (Q) <= 255 characters

Search term. Trimmed; a term shorter than 3 characters yields empty results. Matched case-insensitively as a substring against component name/purl and CVE id. LIKE metacharacters (%, _) are escaped and matched literally.

Kinds (string) or Kinds (null) (Kinds)

Comma-separated categories to search: components, vulnerabilities. Unknown tokens are ignored; omit to search both.

Responses

Response samples

Content type
application/json
{
  • "components": [
    ],
  • "query": "lodash",
  • "vulnerabilities": [
    ]
}

Paged, faceted search results for one kind

Authorizations:
bearerAuth
query Parameters
kind
required
string (Kind) ^(projects|components|vulnerabilities|license...

Which result set to page through.

q
required
string (Q) <= 255 characters
page
integer (Page) [ 1 .. 1000000 ]
Default: 1
size
integer (Size) [ 1 .. 100 ]
Default: 25
Array of Severity (strings) or Severity (null) (Severity)
Array of Status (strings) or Status (null) (Status)
Array of Package Type (strings) or Package Type (null) (Package Type)
Array of License Category (strings) or License Category (null) (License Category)

Responses

Response samples

Content type
application/json
{
  • "counts_capped": false,
  • "facets": {
    },
  • "items_components": [
    ],
  • "items_licenses": [
    ],
  • "items_projects": [
    ],
  • "items_vulnerabilities": [
    ],
  • "kind": "string",
  • "page": 1,
  • "query": "string",
  • "size": 25,
  • "total": 0
}

service-accounts

A team's automation identities

Authorizations:
bearerAuth
query Parameters
team_id
required
string <uuid> (Team Id)

Responses

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "total": 0
}

Create an automation identity for a team

Authorizations:
bearerAuth
Request Body schema: application/json
required
display_name
required
string (Display Name) [ 1 .. 255 ] characters

What this automation is, in words, for whoever reads the audit log.

role
string (Role)
Default: "developer"

The account's role within the team, exactly as a person's would be: 'viewer', 'developer' or 'group_admin'.

slug
required
string (Slug) [ 3 .. 64 ] characters

Lowercase letters, digits and hyphens. Becomes the account's identifier, so it is refused rather than reshaped when it does not fit.

team_id
required
string <uuid> (Team Id)

Responses

Request samples

Content type
application/json
{
  • "display_name": "string",
  • "role": "developer",
  • "slug": "string",
  • "team_id": "810007d0-bec5-486c-b5d1-28fcd8a079ba"
}

Response samples

Content type
application/json
{
  • "created_at": "2019-08-24T14:15:22Z",
  • "email": "string",
  • "full_name": "string",
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "is_active": true,
  • "managed_by_user_id": "d4a34414-11a5-40fc-8142-4b94efd0f141"
}

Stop every key this account holds

Deactivate rather than delete.

The row is the actor on every audit entry its keys produced, and deleting it would either orphan those or cascade them away. Deactivating stops the credentials, which is what the caller is asking for.

Authorizations:
bearerAuth
path Parameters
service_account_id
required
string <uuid> (Service Account Id)

Responses

Response samples

Content type
application/json
{
  • "created_at": "2019-08-24T14:15:22Z",
  • "email": "string",
  • "full_name": "string",
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "is_active": true,
  • "managed_by_user_id": "d4a34414-11a5-40fc-8142-4b94efd0f141"
}

Hand an account to somebody who will answer for it

Authorizations:
bearerAuth
path Parameters
service_account_id
required
string <uuid> (Service Account Id)
Request Body schema: application/json
required
steward_user_id
required
string <uuid> (Steward User Id)

Responses

Request samples

Content type
application/json
{
  • "steward_user_id": "a0441d2b-6572-4f8e-a034-d29f6e6b8ffa"
}

Response samples

Content type
application/json
{
  • "created_at": "2019-08-24T14:15:22Z",
  • "email": "string",
  • "full_name": "string",
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "is_active": true,
  • "managed_by_user_id": "d4a34414-11a5-40fc-8142-4b94efd0f141"
}

transition-approvals

Requests waiting on a decision, for the caller's teams

Scoped to the caller's own teams, so the queue never shows another team's work.

Authorizations:
bearerAuth

Responses

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "total": 0
}

Ask for a status change that needs a second person

Authorizations:
bearerAuth
Request Body schema: application/json
required
finding_id
required
string <uuid> (Finding Id)
justification
required
string (Justification) >= 10 characters

Why the change is being asked for. Required, and required to be substantive: the approver has nothing else to judge.

target_status
required
string (Target Status)

The status being asked for. Must be one the policy names.

Responses

Request samples

Content type
application/json
{
  • "finding_id": "feebf65a-2eaa-4fae-aab2-772450efdffe",
  • "justification": "stringstri",
  • "target_status": "string"
}

Response samples

Content type
application/json
{
  • "created_at": "2019-08-24T14:15:22Z",
  • "decided_at": "2019-08-24T14:15:22Z",
  • "decided_by_user_id": "c750fc7f-0b29-4c93-a0d7-9578b1a62969",
  • "decision_note": "string",
  • "finding_id": "feebf65a-2eaa-4fae-aab2-772450efdffe",
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "justification": "string",
  • "requested_by_user_id": "ed1d5170-d6e9-4e60-8754-78ea9640e9db",
  • "state": "string",
  • "target_status": "string",
  • "team_id": "810007d0-bec5-486c-b5d1-28fcd8a079ba"
}

Agree to or refuse a request somebody else opened

Authorizations:
bearerAuth
path Parameters
approval_id
required
string <uuid> (Approval Id)
Request Body schema: application/json
required
approve
required
boolean (Approve)
Note (string) or Note (null) (Note)

Optional reasoning. Kept whether the answer was yes or no.

Responses

Request samples

Content type
application/json
{
  • "approve": true,
  • "note": "string"
}

Response samples

Content type
application/json
{
  • "created_at": "2019-08-24T14:15:22Z",
  • "decided_at": "2019-08-24T14:15:22Z",
  • "decided_by_user_id": "c750fc7f-0b29-4c93-a0d7-9578b1a62969",
  • "decision_note": "string",
  • "finding_id": "feebf65a-2eaa-4fae-aab2-772450efdffe",
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "justification": "string",
  • "requested_by_user_id": "ed1d5170-d6e9-4e60-8754-78ea9640e9db",
  • "state": "string",
  • "target_status": "string",
  • "team_id": "810007d0-bec5-486c-b5d1-28fcd8a079ba"
}

user-anonymisation

Ask for a user's data to be anonymised

Authorizations:
bearerAuth
Request Body schema: application/json
required
Reason (string) or Reason (null) (Reason)

Why the erasure was asked for, for the operator who will run it and for whoever reviews the decision later. Free text, so do not put the subject's contact details in it.

subject_user_id
required
string <uuid> (Subject User Id)

Responses

Request samples

Content type
application/json
{
  • "reason": "string",
  • "subject_user_id": "1707c861-e9ed-48f7-9f9f-3d21797cfdf4"
}

Response samples

Content type
application/json
{
  • "approved_at": "2019-08-24T14:15:22Z",
  • "approved_by_user_id": "6f927876-90f9-4dae-8888-4b498595faf4",
  • "created_at": "2019-08-24T14:15:22Z",
  • "executed_at": "2019-08-24T14:15:22Z",
  • "expires_at": "2019-08-24T14:15:22Z",
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "reason": "string",
  • "requested_by_user_id": "ed1d5170-d6e9-4e60-8754-78ea9640e9db",
  • "state": "string",
  • "subject_user_id": "1707c861-e9ed-48f7-9f9f-3d21797cfdf4"
}

Approved requests no operator has run yet

Authorizations:
bearerAuth

Responses

Response samples

Content type
application/json
{
  • "count": 0,
  • "items": [
    ]
}

Withdraw a request that has not run

Authorizations:
bearerAuth
path Parameters
request_id
required
string <uuid> (Request Id)

Responses

Response samples

Content type
application/json
{
  • "approved_at": "2019-08-24T14:15:22Z",
  • "approved_by_user_id": "6f927876-90f9-4dae-8888-4b498595faf4",
  • "created_at": "2019-08-24T14:15:22Z",
  • "executed_at": "2019-08-24T14:15:22Z",
  • "expires_at": "2019-08-24T14:15:22Z",
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "reason": "string",
  • "requested_by_user_id": "ed1d5170-d6e9-4e60-8754-78ea9640e9db",
  • "state": "string",
  • "subject_user_id": "1707c861-e9ed-48f7-9f9f-3d21797cfdf4"
}

Agree to a request somebody else opened

Authorizations:
bearerAuth
path Parameters
request_id
required
string <uuid> (Request Id)

Responses

Response samples

Content type
application/json
{
  • "approved_at": "2019-08-24T14:15:22Z",
  • "approved_by_user_id": "6f927876-90f9-4dae-8888-4b498595faf4",
  • "created_at": "2019-08-24T14:15:22Z",
  • "executed_at": "2019-08-24T14:15:22Z",
  • "expires_at": "2019-08-24T14:15:22Z",
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "reason": "string",
  • "requested_by_user_id": "ed1d5170-d6e9-4e60-8754-78ea9640e9db",
  • "state": "string",
  • "subject_user_id": "1707c861-e9ed-48f7-9f9f-3d21797cfdf4"
}

users-me

Download a copy of your own personal data

Keyed off the JWT, never off a parameter.

There is no user_id anywhere in this route. An export endpoint that accepted one would be an endpoint for reading other people's personal data, guarded only by whatever check somebody remembered to write.

Authorizations:
bearerAuth

Responses

Response samples

Content type
application/json
null

Start enrolling a second factor

Store a secret and hand back what the setup screen needs to show it.

Behind a step-up. Enrolling on somebody else's account is a takeover: the attacker's authenticator becomes the factor, and the owner is locked out by the control they never set up.

Authorizations:
bearerAuth
Request Body schema: application/json
required
Code (string) or Code (null) (Code)
Password (string) or Password (null) (Password)

Responses

Request samples

Content type
application/json
{
  • "code": "string",
  • "password": "string"
}

Response samples

Content type
application/json
{
  • "mfa_token": "string",
  • "provisioning_uri": "string",
  • "secret": "string"
}

Finish enrolling by proving the authenticator works

Turn the factor on and return the recovery codes, shown once.

Authorizations:
bearerAuth
Request Body schema: application/json
required
code
required
string (Code) [ 1 .. 64 ] characters
mfa_token
required
string (Mfa Token) [ 1 .. 4096 ] characters

Responses

Request samples

Content type
application/json
{
  • "code": "string",
  • "mfa_token": "string"
}

Response samples

Content type
application/json
{
  • "codes": [
    ]
}

Replace the unused recovery codes with a fresh set

Issue a new set, invalidating every unused code from the old one.

Behind a step-up, because what it hands back is ten sign-ins that bypass the factor, survive a password change, and are not touched by revoking sessions. Gating that on a session alone means a stolen token is worth ten of them, and a stolen session is the case the factor exists to survive.

Authorizations:
bearerAuth
Request Body schema: application/json
required
Code (string) or Code (null) (Code)
Password (string) or Password (null) (Password)

Responses

Request samples

Content type
application/json
{
  • "code": "string",
  • "password": "string"
}

Response samples

Content type
application/json
{
  • "codes": [
    ]
}

Return the caller's notification preferences (creates defaults)

Authorizations:
bearerAuth

Responses

Response samples

Content type
application/json
{
  • "email_enabled": true,
  • "in_app_enabled": true,
  • "slack_enabled": true,
  • "teams_enabled": true
}

Replace the caller's notification preferences (full-row PUT)

Full-row update — every channel field must be supplied.

The body's only meaningful inputs are the four channel toggles. Any additional fields a caller may send (user_id, id, ...) are ignored: Pydantic strips unknown fields by default and the service is keyed off actor.id, never the body.

Chore O / M3 — In-app notifications cannot be disabled. The frontend documents the in-app switch as "rendered but disabled"; this server- side guard closes the API drift where a direct PUT could opt out.

Authorizations:
bearerAuth
Request Body schema: application/json
required
email_enabled
required
boolean (Email Enabled)
in_app_enabled
required
boolean (In App Enabled)
slack_enabled
required
boolean (Slack Enabled)
teams_enabled
required
boolean (Teams Enabled)

Responses

Request samples

Content type
application/json
{
  • "email_enabled": true,
  • "in_app_enabled": true,
  • "slack_enabled": true,
  • "teams_enabled": true
}

Response samples

Content type
application/json
{
  • "email_enabled": true,
  • "in_app_enabled": true,
  • "slack_enabled": true,
  • "teams_enabled": true
}

List the caller's connected OAuth identities (sorted oldest-first)

Authorizations:
bearerAuth

Responses

Response samples

Content type
application/json
{
  • "has_password": true,
  • "items": [
    ]
}

Unlink one of the caller's OAuth identities

Remove an OAuth identity link from the authenticated user.

Returns 204 on success. Domain failures map to RFC 7807:

  • 404 urn:trustedoss:problem:oauth_identity_not_found — identity does not exist OR belongs to another user (existence-hide; the two cases share a shape).
  • 409 urn:trustedoss:problem:oauth_unlink_blocks_login — unlinking would leave the user with no way to authenticate.
Authorizations:
bearerAuth
path Parameters
identity_id
required
string <uuid> (Identity Id)

Responses

Response samples

Content type
application/problem+json
{
  • "detail": "string",
  • "instance": "string",
  • "status": 0,
  • "title": "string",
  • "type": "about:blank"
}

webhooks

Receive a GitHub webhook delivery

Authorizations:
bearerAuth
header Parameters
X-Hub-Signature-256 (string) or X-Hub-Signature-256 (null) (X-Hub-Signature-256)
X-Github-Event (string) or X-Github-Event (null) (X-Github-Event)
X-Github-Delivery (string) or X-Github-Delivery (null) (X-Github-Delivery)

Responses

Response samples

Content type
application/json
null

Receive a GitLab webhook delivery

Authorizations:
bearerAuth
header Parameters
X-Gitlab-Token (string) or X-Gitlab-Token (null) (X-Gitlab-Token)
X-Gitlab-Event (string) or X-Gitlab-Event (null) (X-Gitlab-Event)
X-Gitlab-Webhook-Uuid (string) or X-Gitlab-Webhook-Uuid (null) (X-Gitlab-Webhook-Uuid)

Responses

Response samples

Content type
application/json
null