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

TypeScript SDK

View .md

@zksecurity/zkao-sdk is a typed client for the zkao API. Its types are generated from the OpenAPI spec, so every request and response is checked at compile time. It runs on Node 18 or later and in any runtime with fetch.

Terminal window
npm install @zksecurity/zkao-sdk
import { ZkaoClient } from "@zksecurity/zkao-sdk";
const zkao = new ZkaoClient({
token: process.env.ZKAO_API_TOKEN!,
projectId: process.env.ZKAO_PROJECT_ID!,
});

A client is bound to one project. Every call is scoped to it.

Option Type Meaning
token string Project API token, zkao_proj_<keyId>_<secret>. Required.
projectId string Id of the project the token belongs to. Required.
baseUrl string API base URL, used exactly as given. Optional.
fetch typeof fetch Custom fetch, for tests or proxies. Optional.

The client picks its base URL in this order:

  1. The baseUrl option, used verbatim.
  2. The ZKAO_URL environment variable, normalized.
  3. Production, https://zkao.io/api/v1 (exported as DEFAULT_BASE_URL).

ZKAO_URL accepts a bare host, an origin, or a full API URL. A bare host gets https://, except localhost, which gets http://. /api/v1 is appended unless the value already ends in /api/v<n>. So staging.zkao.io becomes https://staging.zkao.io/api/v1. A plain http:// URL to a non-local host logs a warning, since the token would travel unencrypted.

Two helpers expose this logic:

Function Meaning
normalizeBaseUrl(value: string): string Turn a host, origin, or URL into a full API base URL. Throws on an empty or invalid value.
resolveBaseUrlFromEnv(env?): string | undefined Read and normalize ZKAO_URL, from env or process.env. Returns undefined when unset.

Every method returns a promise. A non-2xx response rejects with ZkaoApiError. The required token scope is noted where a method needs more than read. Each method links to its endpoint in the API reference.

Method Returns Meaning
getTokenInfo() TokenInfo The token’s scopes, repository allowlist, expiry, spend limit, and its project and organization. API

The standalone getTokenInfo({ token, baseUrl?, fetch? }) does the same without a project id. Use it to find which project a token belongs to.

Method Returns Meaning
listRepositories() Repository[] The project’s repositories, or the token’s allowlisted subset. API
waitForRepositoryReady(repositoryId, opts?) Repository Block until the repository’s readiness is ready. See Polling helpers.
Method Returns Meaning
getRepositoryGuidance(repositoryId) RepositoryGuidance Read a repository’s guidance. API
setRepositoryGuidance(repositoryId, content, opts?) SetGuidanceResult Set guidance, or clear it with null. Needs guidance:write. API

setRepositoryGuidance takes { expectedContent?: string | null }. Pass the content you last read, or null if it was cleared. The write then fails with 409 conflict if someone changed it in between. Writing the content already stored is a no-op that returns unchanged: true.

const current = await zkao.getRepositoryGuidance(repoId);
await zkao.setRepositoryGuidance(repoId, `${current.content ?? ""}\nNew note.`, {
expectedContent: current.content,
});
Method Returns Meaning
listAuditAreas(repositoryId, opts?) AuditAreaList A repository’s audit areas. opts.branch picks the branch whose map gives sizes. API
createAuditArea(repositoryId, name, description?) AuditArea Add a custom area. The key is derived from the name. Needs guidance:write. API
deleteAuditArea(repositoryId, areaKey) DeleteAuditAreaResult Delete a custom area. An area a scan’s map named is refused with 409. Needs guidance:write. API
Method Returns Meaning
listScans(opts?) Paginated<Scan> Scans, most recent first. opts takes page and limit (max 100). API
getScan(scanId) ScanDetail One scan’s status and detail. API
launchScan(body) LaunchScanResult Launch a scan. Needs scans:launch. API
waitForScan(scanId, opts?) ScanDetail Block until the scan is COMPLETED, FAILED, or CANCELLED. See Polling helpers.
cancelScan(scanId) CancelScanResult Cancel a running or queued scan and release its unspent credits. Needs scans:launch. API
publishScan(scanId, opts?) PublishArtifactResult Publish a completed scan as a public page. opts.withPassword adds a generated password. Needs publish. API

launchScan takes a LaunchScanRequest:

Field Meaning
repositoryId Repository to scan. Required.
creditBudget Maximum budget in credits. Omit it to use the budget zkao recommends.
presetRef Preset ref from listScanPresets(). Defaults to the first active preset.
branch Branch to scan. Ignored when commitHash is set.
commitHash Commit to scan.
commitMessage Commit message to record.
baseCommit Base commit, branch, or tag of a diff scan. Required by a diff preset, refused by others.
auditAreaKeys Area keys to scope the scan to. Omit to scan the whole repository.
guidance Guidance for this scan only, replacing the repository’s.

It resolves to { scanId, queued, creditBudget }. See Scans and Diff scans.

findingId accepts the full id or the ZK- label shown on the finding page.

Method Returns Meaning
listFindings(opts?) Paginated<Finding> Findings across the project. opts takes scanId, page, and limit. API
getFinding(findingId) FindingDetail Full detail, including the proof of concept and notes. API
addFindingNote(findingId, content) { noteId, findingId } Add a comment. Needs findings:write. API
pinFindingNote(findingId, noteId, kind?) { findingId, noteId } Pin a note as the finding’s resolution note. kind is "resolution". Needs findings:write. API
setFindingSeverity(findingId, severity) { findingId, userSeverity, effectiveSeverity } Override severity, or clear the override with null. Needs findings:write. API
setFindingResolution(findingId, status, opts?) { findingId, resolutionStatus } Change the resolution status. Needs findings:write. API
publishFinding(findingId, opts?) PublishArtifactResult Publish a finding as a public page. opts takes noteId and withPassword. Needs publish. API

setFindingResolution takes { note?: ChangeNote; reason?: ResolutionReason }. note is { content } for a new comment or { existingNoteId } to reuse one. reason is a catalog code valid for the status. note wins when both are given. Both are ignored for NOT_STARTED and IN_PROGRESS. See Triage findings for the codes.

await zkao.setFindingResolution(findingId, "WONT_FIX", { reason: "risk_accepted" });
Method Returns Meaning
listScanPresets() ScanPreset[] Presets the project can launch. Pass a ref as presetRef. API

The balance belongs to the project’s organization and is shared by all its projects. Usage and the monthly summary count only movements attributed to this project. See Credits.

Method Returns Meaning
getBillingBalance() BillingBalance balanceCredits, reservedCredits held by active scans, and availableCredits for new scans. API
getBillingUsage(opts?) BillingUsage Ledger movements with signed credits. opts takes from, to, and limit. Defaults to the last 30 days. API
getBillingSummary(opts?) UsageMonthSummary[] Credits spent and purchased per UTC month, newest first. opts.months sets how many. API

Scans take minutes, and a tight polling loop is rejected with 429. The helpers back off exponentially with jitter, honor the server’s Retry-After, and treat a 429 as a signal to slow down.

const { scanId } = await zkao.launchScan({ repositoryId, presetRef });
const scan = await zkao.waitForScan(scanId, {
onPoll: (s) => console.log(s.status),
});
if (scan.status !== "COMPLETED") throw new Error(`Scan ended ${scan.status}`);

waitForScan(scanId, opts) resolves with the final detail whatever the final status is.

Option Default Meaning
intervalMs 5000 First delay between polls. Grows by 1.5x each poll.
maxIntervalMs 30000 Cap on the delay.
timeoutMs 1 hour Give up and throw after this long.
signal none AbortSignal that stops the wait. Rejects with the abort reason.
onPoll none Called with the latest ScanDetail on each poll that is not final.

waitForRepositoryReady(repositoryId, opts) takes the same options except onPoll, with a default timeout of 30 minutes. It throws if the repository is not in the project or the token’s allowlist. Call it between adding a repository and launching its first scan.

isTerminalScanStatus(status) and TERMINAL_SCAN_STATUSES tell whether a status is final, for your own loops.

Every non-2xx response throws ZkaoApiError.

Property Meaning
status HTTP status.
code Machine-readable error code from the response, such as insufficient_credits.
message Human-readable message.

Branch on code, not on the message.

import { ZkaoApiError } from "@zksecurity/zkao-sdk";
try {
await zkao.launchScan({ repositoryId, presetRef });
} catch (err) {
if (err instanceof ZkaoApiError && err.code === "repository_initializing") {
await zkao.waitForRepositoryReady(repositoryId);
} else if (err instanceof ZkaoApiError && err.code === "insufficient_credits") {
console.error("Not enough credits for this budget.");
} else {
throw err;
}
}

The full list of codes is in API conventions. Timeouts and aborts in the polling helpers throw a plain Error, not ZkaoApiError.

The package exports a named type for each API object.

Type Meaning
Repository, RepositoryGuidance, SetGuidanceResult Repositories and their guidance.
Scan, ScanDetail, ScanStatus, ScanPreset Scans, their status, and presets.
LaunchScanRequest, LaunchScanResult, CancelScanResult Launch and cancel bodies and results.
Finding, FindingDetail, FindingNote, ChangeNote Findings and their notes.
Severity, ResolutionStatus, ResolutionReason, TriageStatus Finding enums.
PublishArtifactResult Result of publishing a scan or finding.
TokenInfo The calling token and its project.
BillingBalance, BillingUsage, UsageEvent, UsageEventType, BillingSummary, UsageMonthSummary Credit balance and ledger.
Paginated<T> { items, page, limit, total } for list endpoints.
ZkaoClientOptions, WaitForScanOptions, WaitForRepositoryReadyOptions Option objects.

The raw generated types are exported as components and paths. Any schema without a named export is available as components["schemas"]["<Name>"], for example components["schemas"]["AuditArea"].