API, tokens and service accounts
DVSentry has one HTTP API. The web app uses it, and so can your scripts and CI. This page explains authentication, the stable /v1 surface and how to call it safely. The full list of v1 operations, grouped by scope, is the API reference at /api-reference.
The stable API: /v1
/v1/<path> is served by the same handler as /<path>. The v1 surface is exactly the routes a token scope opens (below) plus GET /v1/auth/me; any other /v1/... path answers 404, so sign-in, account, billing, admin and Creator routes are never part of v1. Every v1 response carries DVSentry-API-Version: 1.
Compatibility promise. Within v1 changes are additive only: new operations, new optional fields and parameters, new response fields and new enum values (ignore what you do not know). A removal is announced at least 180 days ahead with Deprecation, Sunset and Link: rel="successor-version" headers, and answers 410 Gone (API_SUNSET) after the sunset. No v1 operation is deprecated today.
Unprefixed routes keep working but carry no stability promise to integrators.
Personal access tokens
Create one under Settings → Personal access tokens (MFA step-up if you have MFA), or:
POST /auth/personal-access-tokens (signed-in browser session)
{"name": "ci-payments", "scopes": "api:read,api:write", "expires_in_days": 90,
"organization_id": "6b0d1c9e-..."}
- The token (
dvs_pat_<hex>) is shown once; DVSentry stores only its SHA-256. - Tokens always expire (1-365 days, default 90). Limit a CI token to one organization with
organization_id. - Tokens cannot reach
/creator/*or most/auth/*routes, and cannot create organizations. - List and revoke tokens on the same page (
GET/DELETE /auth/personal-access-tokens/{id}).
Use it as a bearer token:
curl -sS -H "Authorization: Bearer $DVSENTRY_TOKEN" https://app.dvsentry.com/v1/auth/me
Bearer-token clients need no CSRF token (browser sessions do).
Scopes
A token holds either the legacy scopes or fine-grained ones, never both.
- Legacy:
api:readadmits GET;api:writeeverything else the user may do. - Fine-grained (where available; ask support during the beta): an allow-list of routes. A route no scope names is refused, so new endpoints stay closed until given a scope.
GET /auth/token-scopeslists what DVSentry accepts.
| Scope | Opens |
|---|---|
org:read | List the organization, its enabled capabilities and the scanners DVSentry can run |
repositories:read | Repositories, their scan policy, schedules, baselines, regression and scorecard |
repositories:write | Register repositories; change scan policy and baseline |
scans:read | Repository, web, cloud, AI and container image scans |
scans:trigger | Start and cancel scans of every pillar; run a schedule now |
findings:read | Read findings and secret incidents |
findings:write | Triage findings (status, assignee, notes) |
reports:read | Security reports, summaries, regression, compliance, SBOM, VEX, coverage, signed evidence |
reports:export | Exports, export jobs and artifact downloads |
gates:read | CI gate, CI result, policy result, release gate evaluations and evidence |
gates:evaluate | Evaluate a report against a release baseline |
ide:read | Only GET /ide/findings (the IDE extension's token) |
A typical CI token needs scans:trigger, scans:read and gates:read. Add findings:read to print findings and reports:export to download SARIF.
Service accounts
A service account is an organization-owned identity for CI that survives the person who created it leaving. Available on request during the beta; the API is ready, a settings page is not built yet.
- Created by an Admin or Owner from a signed-in session (never by a token), role
viewerormemberonly, never Admin. - Never signs in and is not a billable seat. Repository access is through teams, like any member.
- Its tokens are ordinary PATs confined to the organization.
POST /organizations/{org}/service-accounts {"name": "github-actions", "role": "member"}
POST /organizations/{org}/service-accounts/{sa}/tokens {"name": "payments-ci",
"scopes": "scans:trigger,scans:read,gates:read",
"expires_in_days": 180}
Token IP allow-list
An Admin can restrict every token of the organization to CIDR ranges (where available): PUT /organizations/{org}/token-ip-allowlist. Saved rules stay enforced.
Calling the API well
- Retry-safe writes. Send an
Idempotency-Keyheader on POSTs. A retry with the same key and body replays the first answer (Idempotent-Replayed: true); a different body is 422; a request still running is 409. Use your CI build id. - Rate limits answer 429 with
Retry-After. Retry reads on 429, 502, 503, 504 with backoff; never in parallel. - Not found means not yours. A resource of another organization answers 404, the same as one that does not exist. A role that is too low answers 403.
- Errors are JSON:
{"detail": "...", "code": "..."}where a stable code exists (for exampleINVOICE_BILLING,API_SUNSET,REQUEST_TOO_LARGE). - Pagination: list routes take
limitandoffset. - Redirects: do not follow redirects with a token attached (the CLI never does).
Example: from zero to a gated scan with curl
export DVSENTRY_URL=https://app.dvsentry.com
export DVSENTRY_TOKEN=dvs_pat_...
curl -sS -H "Authorization: Bearer $DVSENTRY_TOKEN" "$DVSENTRY_URL/v1/repositories?limit=50"
curl -sS -H "Authorization: Bearer $DVSENTRY_TOKEN" "$DVSENTRY_URL/v1/scanner-capabilities"
REPORT=$(curl -sS -X POST "$DVSENTRY_URL/v1/security-reports" \
-H "Authorization: Bearer $DVSENTRY_TOKEN" -H "Content-Type: application/json" \
-H "Idempotency-Key: $BUILD_ID" \
-d "{\"repository_id\":\"$REPO_ID\",\"scanners\":[\"semgrep-local\",\"gitleaks\",\"osv-dependencies\"],\"git_ref\":\"$GIT_SHA\"}" \
| python3 -c 'import json,sys; print(json.load(sys.stdin)["id"])')
until curl -sS -H "Authorization: Bearer $DVSENTRY_TOKEN" "$DVSENTRY_URL/v1/security-reports/$REPORT" \
| grep -Eq '"status": *"(completed|partial|failed|cancelled|timed_out)"'; do sleep 15; done
curl -sS -H "Authorization: Bearer $DVSENTRY_TOKEN" "$DVSENTRY_URL/v1/security-reports/$REPORT/ci-result"
The CLI does all of this, with retries and exit codes, in one command.
OpenAPI
The OpenAPI document repeats each v1 operation under /v1 (operationId v1_<id>, extension x-dvsentry-scope). The interactive Swagger page is at /api-docs and the published reference at /api-reference.
Something missing or unclear? Tell us with Feedback in the app.