SDK client
Every method, option, type and error code of the @patchlight/sdk TypeScript client.
import { createClient, PatchlightError } from "@patchlight/sdk";
const client = createClient({ apiKey: process.env.PATCHLIGHT_API_KEY! });Client options
Prop
Type
Reviews
| Method | Calls | Returns |
|---|---|---|
createReview(input, options?) | POST /v1/reviews | { id, status } |
getReview(id) | GET /v1/reviews/:id | status, summary, error, timestamps |
getReviewFindings(id) | GET /v1/reviews/:id/findings | { status, findings }, empty until done |
waitForReview(id, options?) | polls getReview | the review once done |
listReviews() | GET /v1/reviews | recent reviews |
waitForReview throws review_failed, review_skipped or timeout instead of polling forever.
createReview input
| Field | Type | Description |
|---|---|---|
repo | string | Repository name shown in the dashboard, e.g. "owner/repo". Max 256 chars |
diff | string | Unified diff. Max 1 MB |
files | Record<string, string> | Optional. Path to content, for review context. Max 100 files, 200 KB each, 5 MB total |
ref | string | Optional. Reviewed commit sha or branch |
baseRef | string | Optional. Base branch of the diff |
title | string | Optional. Review title |
createReview options
| Field | Description |
|---|---|
idempotencyKey | A repeat with the same key returns the original review instead of a duplicate. Also lets the submit retry on transient errors |
Scans
| Method | Calls | Returns |
|---|---|---|
startScan(repoId) | POST /v1/repos/:id/scan | { id, status } |
getScan(id) | GET /v1/scans/:id | status, cost, findingsCount |
getScanFindings(id) | GET /v1/scans/:id/findings | { status, findings }, empty until done |
waitForScan(id, options?) | polls getScan | the scan once done |
repoId is an id from listRepos(), not an "owner/repo" name. Scans need a repository
connected through the GitHub App; one created by createReview cannot be scanned.
waitForScan throws scan_failed or timeout.
Wait options
Both waitForReview and waitForScan accept:
| Field | Default | Description |
|---|---|---|
timeoutMs | 900000 (15 min) | Overall deadline |
intervalMs | 5000 | Poll interval |
signal | none | AbortSignal to stop waiting |
Findings and workspace
| Method | Calls | Returns |
|---|---|---|
listFindings() | GET /v1/findings | recent findings, review and scan alike |
updateFinding(id, status) | PATCH /v1/findings/:id | { id, status } |
listRepos() | GET /v1/repos | repositories in the workspace |
getBalance() | GET /v1/balance | remaining USD balance |
updateFinding takes "open", "resolved" or "dismissed", and accepts review and scan finding
ids alike.
Types
A review ends in one of three statuses:
| Status | Meaning |
|---|---|
done | Finished. The summary and findings are available |
failed | The worker errored. The reason is in error |
skipped | A spend cap or repository budget blocked it before any work started. It never advances |
A Finding has the same shape wherever it comes from. Exactly one of reviewId and scanId is
set, and cwe is only set on scan findings.
Errors
Every failure throws a PatchlightError with a code, an HTTP status and a message.
try {
await client.createReview({ repo, diff });
} catch (err) {
if (err instanceof PatchlightError) {
console.error(err.code, err.status, err.message); // "insufficient_funds", 402, ...
}
}code | HTTP | Meaning |
|---|---|---|
invalid_request | 400 | Malformed body. Details in message |
repo_not_scannable | 400 | Scan requested for a repo with no GitHub connection |
missing_api_key / invalid_api_key | 401 | Absent or unknown key |
revoked_key / expired_token | 401 | Key revoked, or a patchlight login session expired |
insufficient_funds | 402 | Balance below the cost of the operation |
spend_cap_reached | 402 | Review blocked by the monthly workspace cap or a repository budget |
spend_cap / repo_budget | 402 | Scan blocked by the monthly workspace cap or a repository budget |
not_found / repo_not_found | 404 | Does not exist, or belongs to another workspace |
already_running | 409 | That repository already has a scan queued or running |
payload_too_large | 413 | Diff or file limits exceeded |
too_many_pending_reviews | 429 | More than 10 API reviews queued or running in the workspace |
queue_unavailable / reviews_not_configured / scan_runner_not_configured | 503 | Temporary platform issue. Retry later |
network_error / http_5xx | none | Transport or server failure, after retries |
review_failed / review_skipped / timeout / aborted | none | From waitForReview |
scan_failed / timeout | none | From waitForScan |
Retries
- GET requests retry up to 3 times, with exponential backoff, on network errors and 5xx responses.
createReviewonly retries when anidempotencyKeymakes a repeat safe.startScannever retries. The API rejects a second concurrent scan withalready_running, so a retry would report a conflict rather than the original scan.
On a 429, err.retryAfterSeconds holds the response's Retry-After value (or null), so you
can wait exactly as long as the API asked:
catch (err) {
if (err instanceof PatchlightError && err.retryAfterSeconds) {
await sleep(err.retryAfterSeconds * 1000);
}
}