Skip to content
These docs describe staging.zkao.io and the @zksecurity/zkao-cli@next release. For production, see docs.zkao.io.

API conventions

View .md

These rules apply to every endpoint in the API reference. The machine-readable contract is the OpenAPI spec at /openapi/v1.yaml.

https://zkao.io/api/v1

Project resources live under /projects/{projectId}, for example /projects/{projectId}/scans. The one exception is GET /token, which needs no project id. Staging uses https://staging.zkao.io/api/v1. See Environments.

Send a project API token in the Authorization header:

Authorization: Bearer zkao_proj_<keyId>_<secret>

A token in the query string is rejected with 400. See Authentication for how to get a token and what its scopes allow.

Request and response bodies are JSON. Send Content-Type: application/json with a body. Timestamps are ISO 8601 strings.

Every error uses one envelope:

{ "error": { "code": "not_found", "message": "Not found" } }

Branch on code. The message is for people and can change.

Status Code Meaning
400 bad_request The request is invalid. The message says why.
400 diff_base_required, diff_base_not_allowed, diff_base_invalid A diff scan’s base is missing, given to a preset that refuses it, or malformed.
401 unauthorized The token is missing, malformed, expired, or revoked.
402 insufficient_credits The organization’s credits cannot cover the scan.
403 forbidden The token lacks the scope this call needs.
404 not_found The resource does not exist, or is outside the token’s project or repository allowlist.
409 conflict A concurrent change, such as guidance edited since you read it, or an ambiguous ZK- label.
409 repository_initializing The repository is still being analyzed. Retry once its readiness is ready.
422 diff_empty A diff scan has no change to audit between the base and the head.
429 rate_limited Too many requests, or the token’s spend limit is reached.
500 internal Something failed on zkao’s side.

zkao answers 404, not 403, for anything outside a token’s reach. That way a token cannot probe which ids exist.

The SDK throws ZkaoApiError for any non-2xx response, with .status, .code, and .message. The CLI prints Error <status> (<code>): <message> on stderr and exits with code 1.

List endpoints for scans and findings take page and limit query parameters:

Terminal window
curl -H "Authorization: Bearer $ZKAO_API_TOKEN" \
"https://zkao.io/api/v1/projects/$ZKAO_PROJECT_ID/findings?page=2&limit=100"
  • page starts at 1.
  • limit defaults to 50 and is capped at 100.

They return:

{ "items": [], "page": 2, "limit": 100, "total": 312 }

Keep requesting pages until page * limit >= total. Other lists, such as repositories and presets, return everything in one response.

Each token has a request ceiling per time window. Normal use never reaches it. Past it, the API returns 429 rate_limited with a Retry-After header in seconds. Wait that long before retrying.

While a scan is still running, GET /scans/{scanId} returns an advisory Retry-After header. It says how long to wait before polling again. Honor it instead of polling in a tight loop. The SDK’s waitForScan and the CLI’s zkao scans wait do this for you.

A launch that would push a token past its spend limit also returns 429.

Ids are opaque strings. Treat them as such.

A finding also has a ZK- label, shown on its page. The label is the id’s last eight characters. Every endpoint that takes a findingId also accepts the label, with or without the ZK- prefix. A label that matches more than one finding in the project returns 409 conflict. Use the full id then.

Projects, organizations, and repositories also carry a slug. Slugs form the readable app URLs, such as /orgs/<org>/projects/<project>/repos/<repo>. API paths use ids, not slugs.

Every budget, balance, and spend in the API is in credits. Credits are the only unit the API reports. See Credits and billing.

The major version is part of the path: /api/v1. New endpoints and new response fields can appear within v1. Ignore fields your client does not recognize.