Skip to content

API documentation

Start scans, read results and plug them into CI. Authenticate with an API key in the `X-API-Key` header (create one under Settings → Security; scopes: scan:read, scan:write, report:read, project:read). Responses use `{ "success": true, "data": … }`; errors use `{ "success": false, "error": "…" }`. Every request counts against your plan's per-minute (per key) and per-day (per workspace) limits.

Machine-readable spec: /api/v1/openapi.json (OpenAPI 3.1.0, version 1.0.0)

Quick start

# 1. start a scan (the Idempotency-Key makes a retry safe) curl -X POST $WCS_URL/api/v1/scans \ -H "X-API-Key: $WCS_API_KEY" -H "Content-Type: application/json" \ -H "Idempotency-Key: build-1234" -H "X-WCS-Client: cli" \ -d '{"url":"https://example.com","level":"quick"}' # 2. wait until "status" is "complete" (send If-None-Match with the last ETag: unchanged answers 304) curl -H "X-API-Key: $WCS_API_KEY" $WCS_URL/api/v1/scans/SCAN_ID # 3. read the result with a pass/fail verdict curl -H "X-API-Key: $WCS_API_KEY" "$WCS_URL/api/v1/scans/SCAN_ID/report?failUnder=80&failOn=critical"

Command line

npx @wcs/cli scan https://example.com --fail-under 80 --fail-on critical --format sarif --output wcs.sarif # exit code 0 = passed, 1 = did not pass, 2 = error (bad key, quota, network)

GitHub Actions

- uses: YOUR_ORG/wcs-tools/packages/github-action@v1 with: url: https://example.com api-url: https://your-wcs-host api-key: ${{ secrets.WCS_API_KEY }} fail-under: 80 fail-on: critical sarif-file: wcs.sarif - uses: github/codeql-action/upload-sarif@v3 if: always() with: { sarif_file: wcs.sarif }

Limits

  • Requests: per API key per minute and per workspace per day, by plan (Pro 120 and 5,000; Teams 300 and 20,000; Enterprise 1,000 and no daily cap). Every response carries RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset; at the limit you get 429 with Retry-After.
  • Scans: every scan started through the API counts against the same monthly quota as scans started in the app.
  • Polling: use If-None-Match with the ETag from the last response; an unchanged scan answers 304. Checking every 5 to 10 seconds is plenty.
  • Scopes: a key can only call endpoints for its scopes; the required scope is shown on each endpoint.

Endpoints

post/api/v1/scans

Start a scan scope: scan:write

Counts against the monthly scan quota like any other scan. Send an `Idempotency-Key` to make retries safe. Set `X-WCS-Client` (`cli`, `github-action`) so usage can be reported by source.

ParameterInNotes
Idempotency-KeyheaderSame key and same body within 24 hours returns the first response (header `Idempotent-Replayed: true`). Same key with a different body returns 422.
X-WCS-Clientheaderone of cli, github-action, github-app, other

Responses: 201, 400, 401, 402, 403, 409, 422, 429

get/api/v1/scans

List scans scope: scan:read

ParameterInNotes
limitquerydefault 20
cursorquery
projectIdquery
statusqueryone of queued, crawling, scanning, generating_report, complete, failed, cancelled

Responses: 200, 400, 401, 403, 429

get/api/v1/scans/{scanId}

Get a scan scope: scan:read

Poll with `If-None-Match` set to the last `ETag`: an unchanged scan answers `304` with no body.

ParameterInNotes
scanId *path
If-None-Matchheader

Responses: 200, 304, 401, 403, 404, 429

get/api/v1/scans/{scanId}/report

Get a finished scan as JSON, SARIF or JUnit scope: report:read

`format=summary` (default) is a compact result with a pass/fail verdict for `failUnder` (minimum score) and `failOn` (fail if an issue of this severity or worse exists). `sarif` returns SARIF 2.1.0; `junit` returns JUnit XML. The HTTP status is 200 whether or not the verdict is a pass.

ParameterInNotes
scanId *path
formatqueryone of summary, json, sarif, junit. default summary
failUnderquery
failOnqueryone of critical, high, medium, low, none. default none

Responses: 200, 401, 403, 404, 409, 429

get/api/v1/projects

List projects scope: project:read

Responses: 200, 401, 403, 429

get/api/v1/projects/{projectId}/findings

List tracked findings of a project scope: project:read

ParameterInNotes
projectId *path
statequeryone of open, acknowledged, ignored, accepted_risk, false_positive, fixed
severityqueryone of critical, high, medium, low, info
limitquerydefault 50

Responses: 200, 401, 403, 404, 429

Using the results in CI

format=sarif returns SARIF 2.1.0 for code-scanning dashboards. Findings point at web pages, not repository files, so they appear in the Security tab of the repository but cannot be shown as line annotations. format=junit returns one test suite per scanner; with failOn set, issues of that severity or worse are reported as failures.