REST API
Endpoints, request shapes, and error codes for the Patchlight API.
Base URL https://api.patchlight.dev. Every /v1/* route expects a workspace API key:
curl https://api.patchlight.dev/v1/balance \
-H "Authorization: Bearer pl_sk_..."Create the key in the dashboard under App → API Keys. If TypeScript is where you live, the SDK wraps all of this — including polling, retries, and idempotency — and has no runtime dependencies.
Webhooks (inbound)
Single signature-verified receiver, configured once on your GitHub App (one webhook for all installs).
| Method | Path | Description |
|---|---|---|
POST | /webhooks/github | Verifies X-Hub-Signature-256, records the event, enqueues a review |
Reviews API
Trigger reviews from any CI/CD system — see the SDK & CLI docs for the friendly
wrapper. All /v1/* routes are scoped to the API key's workspace: an organization key sees the
organization's data, a personal key sees personal data only.
| Method | Path | Description |
|---|---|---|
POST | /v1/reviews | Submit a unified diff (+ optional file contents) for review. Returns 202 with { review: { id, status } } |
GET | /v1/reviews/:id | Review status, summary, error, timestamps |
GET | /v1/reviews/:id/findings | { status, findings } — findings are empty until the review is done |
POST /v1/reviews body
| Field | Required | Limits | Description |
|---|---|---|---|
repo | yes | ≤256 chars, [\w./@-] | Repository name shown in the dashboard |
diff | yes | ≤1 MB | Unified diff to review |
files | no | ≤100 files, ≤200 KB each, ≤5 MB total | Path → content map for review context |
ref | no | ≤200 chars | Reviewed commit sha/branch |
baseRef | no | ≤200 chars | Base branch of the diff |
title | no | ≤500 chars | Human-readable title |
Send an Idempotency-Key header to make the submit replay-safe for 24 hours: repeats with the
same key return the original review (200) instead of creating a duplicate.
Error codes
| Status | error | Meaning |
|---|---|---|
400 | invalid_request | Malformed body — details in message |
401 | missing_api_key / invalid_api_key | Absent, unknown, or revoked key |
402 | insufficient_funds | Balance below the review cost (body includes balance, required, in USD) |
404 | not_found | Review does not exist in this workspace |
413 | payload_too_large | Diff or file caps exceeded |
429 | too_many_pending_reviews | More than 10 queued/running API reviews |
503 | queue_unavailable / reviews_not_configured | Transient platform issue — retry later |
Review status
queued → running → done. Two other statuses are terminal:
failed— the worker errored; the reason is inerror.skipped— a monthly spend cap or per-repo budget blocked the review before any work started. It never advances, so polling fordonewill hit your timeout instead.
Security scans API
A scan reads a whole repository rather than a diff, so it requires a connected GitHub
repository — repos created implicitly by POST /v1/reviews have no git connection and cannot be
scanned. Scans bill per token, so cost scales with repository size.
| Method | Path | Description |
|---|---|---|
POST | /v1/repos/:id/scan | Start a scan. Returns 202 with { scanId, status } |
GET | /v1/scans/:id | Scan status, cost, and findingsCount |
GET | /v1/scans/:id/findings | { status, findings } — empty until the scan is done |
:id is a repository id from GET /v1/repos, not an owner/repo name.
Scan error codes
| Status | error | Meaning |
|---|---|---|
400 | repo_not_scannable | Repo has no GitHub connection |
402 | insufficient_funds | Balance below the minimum to start a scan |
402 | spend_cap / repo_budget | Monthly workspace cap or per-repo budget reached |
404 | repo_not_found | Repo does not exist in this workspace |
409 | already_running | That repo already has a queued or running scan |
503 | scan_runner_not_configured | Scan runner unavailable — retry later |
Findings API
| Method | Path | Description |
|---|---|---|
GET | /v1/findings | Recent findings across the workspace's repositories |
PATCH | /v1/findings/:id | Triage a finding — { "status": "open" | "resolved" | "dismissed" } |
PATCH /v1/findings/:id accepts both review and scan finding ids; you do not need to know which
kind you hold. It returns { finding: { id, status } }, or 404 when the finding is not in the
key's workspace.
Every route that returns findings uses one shape. Exactly one of reviewId / scanId is set and
the other is null; cwe is only ever populated by scans.
Org-scoped API keys can triage anything in the organization. The dashboard additionally limits members to findings on their own pull requests — an API key resolves to a workspace rather than to a person, so that narrowing has no equivalent here.
Read API
| Method | Path | Description |
|---|---|---|
GET | /v1/balance | Remaining USD balance of the workspace |
GET | /v1/repos | Repositories in the workspace |
GET | /v1/reviews | Recent reviews — same fields as GET /v1/reviews/:id |
GET | /v1/findings | Findings across the workspace's repositories |
GET | /health | Health check |
Reviews and security scans are billed in USD from your balance.
All timestamps (createdAt, startedAt, completedAt, finishedAt) are ISO-8601 strings,
not epoch numbers. Parse them with new Date(value).