Authentication
Every call to the zkao API uses a project API token. A token belongs to exactly one project. It carries a set of scopes and can be narrowed further to some repositories, an expiry date, and a spend limit.
There are two ways to get one: approve the CLI in your browser, or create a token by hand.
Browser approval
Section titled “Browser approval”zkao loginThe CLI starts a device authorization flow:
- It prints a URL and a short code, and opens the URL in your browser.
- On the Authorize a CLI page, you confirm that the code matches the one in your terminal.
- You pick a project, review the requested scopes, and approve. You can also set a repository allowlist, an expiry, and a spend limit there.
- The CLI receives the token and saves it to
~/.zkao/config.json.
Only approve a login you started yourself. Approving mints a real token for the project you select.
By default the CLI requests read, scans:launch, and findings:write.
The approval page pre-ticks them, and the approver can change them.
To request other scopes, pass --scope:
zkao login --scope read guidance:writezkao login --project <projectId> preselects a project on the approval page.
You can approve several projects in one login. Each gets its own saved token, and the first becomes the active project.
Use --no-browser to print the URL without opening a browser.
Agents and CI
Section titled “Agents and CI”A bare zkao login blocks until someone approves in the browser.
That can take minutes and trip a command timeout.
Non-interactive callers split it in two:
zkao login --no-wait --no-browser # prints the URL and code, then exits# a person opens the URL and approveszkao login --resume # polls once and exitsRepeat zkao login --resume until it prints Authorized.
Still waiting for approval means nobody has approved yet.
--resume --timeout <seconds> waits up to that long instead of polling once.
Several logins can be pending at once. --resume finishes whichever is approved first and keeps the rest.
A pending login expires after a while. Start a new one when --resume reports that it expired.
Tokens created by hand
Section titled “Tokens created by hand”A project admin can create a token under Project Settings → Integrations in the zkao app. Choose a name, the scopes, and optionally a repository allowlist, an expiry, and a spend limit.
The token is shown once, together with the project id. It looks like this:
zkao_proj_<keyId>_<secret>Store it as a secret. zkao cannot show it again.
Hand it to the CLI or SDK through environment variables:
export ZKAO_API_TOKEN=zkao_proj_...export ZKAO_PROJECT_ID=<projectId>Or save it to the config file:
zkao config set --token zkao_proj_... --project <projectId>Scopes
Section titled “Scopes”A token carries a subset of these scopes:
| Scope | Allows |
|---|---|
read |
List and read repositories, scans, findings, presets, and billing. |
findings:write |
Comment on findings, change severity and resolution, pin a note. |
guidance:write |
Update a repository’s guidance and manage its audit areas. |
scans:launch |
Launch and cancel scans. |
publish |
Publish a finding or a scan as a public page. |
A call that needs a scope the token lacks returns 403 forbidden.
Limits on a token
Section titled “Limits on a token”- Repository allowlist. The token only sees the listed repositories. An empty list means every repository in the project.
- Expiry. The token stops working after this date.
- Spend limit. The most credits the token may commit to scans in the current billing period. A launch past the limit returns
429.
zkao whoami (or GET /token) returns the token’s project, organization, scopes, allowlist, expiry, and spend limit.
Any valid token can call it, whatever its scopes.
Errors
Section titled “Errors”| Status | Meaning |
|---|---|
401 unauthorized |
The token is missing, malformed, expired, or revoked. The response does not say which. |
403 forbidden |
The token is valid but lacks the scope this call needs. |
404 not_found |
The resource is outside the token’s project or repository allowlist. zkao does not confirm that it exists. |
A token used against another project’s id gets a 404 whose message names the token’s own project.
Send the token only in the Authorization: Bearer header.
A token passed in the query string is rejected with 400.
Where the CLI stores credentials
Section titled “Where the CLI stores credentials”The CLI writes ~/.zkao/config.json with owner-only permissions.
It keeps one token per project, so logging in to a second project does not drop the first.
zkao config show # active settings and every saved project (tokens masked)zkao config use <projectId> # switch the active projectSettings resolve in this order, highest first:
- Flags:
--token,--project,--base-url. - Environment:
ZKAO_API_TOKEN,ZKAO_PROJECT_ID,ZKAO_URL. - The saved config file.
Revoking a token
Section titled “Revoking a token”Revoke a token under Project Settings → Integrations. It stops working at once. A token also stops working when its project or organization is archived.

