Patchlightdocs

REST API

Endpoints, request shapes and error codes of the Patchlight API.

curl https://api.patchlight.dev/v1/balance \
  -H "Authorization: Bearer pl_sk_..."

Every /v1 route takes a workspace API key from the API Keys page of the dashboard and only sees that workspace: an organization key sees the organization's data, a personal key only personal data.

The SDK wraps all of this, including polling, retries and idempotency, with no runtime dependencies.

Endpoints

MethodPathDescription
POST/v1/reviewsSubmit a diff for review
GET/v1/reviewsRecent reviews
GET/v1/reviews/:idReview status, summary, error and timestamps
GET/v1/reviews/:id/findings{ status, findings }, empty until the review is done
POST/v1/repos/:id/scanStart a security scan
GET/v1/scans/:idScan status, cost and findingsCount
GET/v1/scans/:id/findings{ status, findings }, empty until the scan is done
GET/v1/findingsRecent findings across the workspace
PATCH/v1/findings/:idTriage a finding
GET/v1/reposRepositories in the workspace
GET/v1/balanceRemaining USD balance
GET/healthHealth check, no key needed

All timestamps (createdAt, startedAt, completedAt, finishedAt) are ISO-8601 strings.

Reviews

POST /v1/reviews returns 202 with { review: { id, status } }.

FieldRequiredLimitsDescription
repoyes256 chars, [\w./@-]Repository name shown in the dashboard
diffyes1 MBUnified diff to review
filesno100 files, 200 KB each, 5 MB totalPath to content, for review context
refno200 charsReviewed commit sha or branch
baseRefno200 charsBase branch of the diff
titleno500 charsReview title

Send an Idempotency-Key header to make the submit safe to repeat for 24 hours. A repeat with the same key returns the original review with 200 instead of creating a new one.

A review moves queued → running → done, or ends in one of two other final statuses:

  • failed: the worker errored, and the reason is in error.
  • skipped: a monthly spend cap or repository budget blocked it before any work started. It never advances, so polling for done runs into your timeout.
StatuserrorMeaning
400invalid_requestMalformed body. Details in message
401missing_api_key / invalid_api_keyAbsent or unknown key
401revoked_keyThe key was revoked in the dashboard
401expired_tokenA patchlight login session expired. Sign in again
402insufficient_fundsBalance below the review cost. Body includes balance and required, in USD
402spend_cap_reachedMonthly workspace cap or repository budget reached. scope is workspace or repo; body includes capUsd and spentUsd
404not_foundThe review is not in this workspace
413payload_too_largeDiff or file limits exceeded
429too_many_pending_reviewsMore than 10 API reviews queued or running
503queue_unavailable / reviews_not_configuredTemporary platform issue. Retry later

Scans

POST /v1/repos/:id/scan returns 202 with { scanId, status }. :id is a repository id from GET /v1/repos, not an owner/repo name.

A scan reads a whole repository, so it needs one connected through the GitHub App. A repository created by POST /v1/reviews has no git connection and cannot be scanned. Scans are billed per token.

StatuserrorMeaning
400repo_not_scannableThe repository has no GitHub connection
402insufficient_fundsBalance below the minimum to start a scan
402spend_cap / repo_budgetMonthly workspace cap or repository budget reached
404repo_not_foundThe repository is not in this workspace
409already_runningThat repository already has a scan queued or running
424github_disconnectedThe GitHub App was removed or suspended for this account. Reinstall it
503scan_runner_not_configuredScan runner unavailable. Retry later

Findings

Every route that returns findings uses the same shape. Exactly one of reviewId and scanId is set, and cwe is only set on scan findings.

PATCH /v1/findings/:id takes { "status": "open" | "resolved" | "dismissed" } and returns { finding: { id, status } }. It accepts review and scan finding ids alike, and returns 404 when the finding is not in the key's workspace.

On this page