REST reference

Every /api/v2 endpoint: method, path, scopes, parameters, body fields and errors.

The REST API lives at https://v12.sh/api/v2 and acts on the workspace your token reads; Authentication and permissions covers API keys, OAuth and scopes. The OpenAPI 3.1 document is public, needs no token and may be cached for five minutes: https://v12.sh/api/v2/openapi.json.

Requests

  • {repository} in a path is two segments, owner/name: /repositories/acme/api/findings.
  • repository and at go in the query string on every GET that takes them, and on both PATCH operations.
  • Query lists are comma-separated: status=open,fixed.
  • Other POST and PATCH fields go in a JSON body with Content-Type: application/json; any other type returns 415.
  • Bodies are limited to 1 MiB; a larger one returns 413.

Errors

Every error response has the same body. details is optional, and requestId matches the X-Request-Id response header; include it when you ask for help.

{
  "error": {
    "code": "invalid_request",
    "message": "The request is invalid.",
    "details": { "issues": [{ "path": "limit", "message": "Too big: expected number to be <=100" }] }
  },
  "requestId": "e3fbc6dc-1c5e-4b2d-b998-e820319d6cf7"
}
CodeHTTPMeaning
invalid_request400The input, a header, the cursor or the quote is invalid. Validation errors list details.issues.
repository_required400The finding is in several repositories. Pick one from details.repositories.
unauthenticated401The Authorization header is missing or its token is invalid. See WWW-Authenticate.
insufficient_credits402The workspace lacks the credit for the run.
spend_cap_reached402The workspace's monthly spending limit is reached.
insufficient_scope403The token lacks a scope listed in details.required.
forbidden403The token's user has a role that does not allow the action.
not_found404No such resource in the token's workspace, or the finding is not at that position.
method_not_allowed405The path does not take this method. See Allow.
position_unavailable409V12 cannot resolve the position. details.reason says why.
decision_active409A won't-fix or risk-accepted decision blocks this status. details.findings lists the findings.
conflict409The request conflicts with the current state, such as cancelling a completed run.
price_exceeds_max409The price, details.priceCents, is above maxPriceCents.
quote_stale409The quote expired or what it priced changed. Estimate again.
change_refused409The change cannot be reviewed. details.reason is thin, identical, large or detached.
nothing_to_review409Nothing in scope has files to review at this position.
repository_not_runnable409Access to the repository was revoked, or it is not on GitHub. See details.reason.
idempotency_key_reused409The Idempotency-Key was used for a different request.
api_version_retired410/api/v1 is retired. Use /api/v2.
payload_too_large413The body is larger than details.limitBytes.
unsupported_media_type415The body is not application/json.
rate_limited429A rate limit is used up. Wait Retry-After seconds; details.bucket names the limit.
internal_error500An unexpected failure on V12's side.
upstream_error502A GitHub request V12 needed failed.
platform_unavailable503V12's findings service is unavailable, or is rate limiting V12. Wait Retry-After seconds when it is present.
repository_access_unavailable503V12 could not confirm its GitHub access to the repository.
rate_limit_unavailable503Rate limiting is down, so V12 refuses changes and run estimates. Retry after 30 seconds.
public_api_disabled503The API is turned off for now; the web app still works.

platform_unavailable (503) means V12's findings service answered 429, 502, 503 or 504, or did not answer. When the service said when to retry, the response carries Retry-After and the same seconds in details.retryAfterSeconds. A 500 from that service reaches you as internal_error (500).

Me

OperationEndpointScopesMCP tool
Get the token identityGET /meuser:readget_me

Get the token identity

GET /me · operationId getMe · Scopes: user:read · MCP tool get_me

The user, organization and scopes behind this credential, plus the organization credit balance when the user may view billing.

Returns 200 with user, organization, token, credits. Errors: 400, 401, 403, 404, 409, 429, 503 (Errors).

Members

OperationEndpointScopesMCP tool
List organization membersGET /membersuser:readlist_members

List organization members

GET /members · operationId listMembers · Scopes: user:read · MCP tool list_members

Members of the token organization, sorted by username, then id. Use a member id, email or username to assign findings.

Returns 200 with nextCursor, items. Errors: 400, 401, 403, 404, 409, 429, 503 (Errors).

Focuses

OperationEndpointScopesMCP tool
List focusesGET /focusesrepos:readlist_focuses
Get a focusGET /focuses/{focus}repos:readget_focus

List focuses

GET /focuses · operationId listFocuses · Scopes: repos:read · MCP tool list_focuses

Organization-wide focus documents that steer runs and Autopilot rules.

Returns 200 with nextCursor, items. Errors: 400, 401, 403, 404, 409, 429, 503 (Errors).

Get a focus

GET /focuses/{focus} · operationId getFocus · Scopes: repos:read · MCP tool get_focus

One focus by slug, including the instructions its runs follow.

ParameterInTypeRequiredNotes
focuspathstringyesFocus slug; 1–200 characters

Returns 200 with slug, title, emoji, revision, webUrl, instructions. Errors: 400, 401, 403, 404, 409, 429, 503 (Errors).

Repositories

OperationEndpointScopesMCP tool
List repositoriesGET /repositoriesrepos:readlist_repositories
Get a repositoryGET /repositories/{owner}/{repo}repos:readREST only
List branches, pull requests and tagsGET /repositories/{owner}/{repo}/refsrepos:readlist_refs

List repositories

GET /repositories · operationId listRepositories · Scopes: repos:read · MCP tool list_repositories

Repositories registered in the organization, sorted by full name, with their GitHub access and mirror state.

Returns 200 with nextCursor, items. Errors: 400, 401, 403, 404, 409, 429, 503 (Errors).

Get a repository

GET /repositories/{owner}/{repo} · operationId getRepository · Scopes: repos:read · REST only

One registered repository by "owner/name".

ParameterInTypeRequiredNotes
ownerpathstringyesRepository owner
repopathstringyesRepository name

Returns 200 with id, fullName, defaultBranch, access, mirror, webUrl. Errors: 400, 401, 403, 404, 409, 429, 503 (Errors).

List branches, pull requests and tags

GET /repositories/{owner}/{repo}/refs · operationId listRefs · Scopes: repos:read · MCP tool list_refs

Refs V12 has mirrored for a repository, with whether each head was reviewed. Not paginated; narrow the list with q.

ParameterInTypeRequiredNotes
ownerpathstringyesRepository owner
repopathstringyesRepository name
qquerystringnoCase-insensitive name filter; at most 200 characters
limitqueryintegerno1–200; default 100

Returns 200 with defaultBranch, branches, pullRequests, tags, asOf, nextCursor. Errors: 400, 401, 403, 404, 409, 429, 503 (Errors).

Findings

OperationEndpointScopesMCP tool
List a repository's findings at a positionGET /repositories/{owner}/{repo}/findingsfindings:readlist_repository_findings
List organization findingsGET /findingsfindings:readlist_findings
Change findingsPATCH /findingsfindings:writeupdate_findings
Get a findingGET /findings/{finding}findings:readget_finding
Change a findingPATCH /findings/{finding}findings:writeREST only
Comment on a findingPOST /findings/{finding}/commentsfindings:writecomment_on_finding

List a repository's findings at a position

GET /repositories/{owner}/{repo}/findings · operationId listRepositoryFindings · Scopes: findings:read · MCP tool list_repository_findings

The repository inbox at one commit: every finding with its code state there. Without at, the default branch head. The response pins the evaluated commit in position.at; later pages stay on it.

ParameterInTypeRequiredNotes
ownerpathstringyesRepository owner
repopathstringyesRepository name
atquerystringnoRepository position: "default", "branch:<name>[@<sha>]", "pr:<number>[@<sha>]" or "commit:<sha>". Omitted means "default"; at most 512 characters
statusquery("open" | "investigating" | "fixed" | "wont-fix" | "false-positive" | "duplicate")[]noat most 6 items
severityquery("info" | "low" | "medium" | "high" | "critical")[]noat most 5 items
assigneequerystringno"me", "none", a member id, a member email, or a member username. A username several members share matches all of them; 1–320 characters
focusquerystringnoFocus slug; 1–200 characters
dismissedquerybooleannotrue lists only dismissed findings; false (default) hides them; default false
limitqueryintegerno1–100; default 50
cursorquerystringno`nextCursor` from the previous page; 1–2048 characters

Returns 200 with nextCursor, items, repository, position, reviewCoverage, counts, total, asOf. Errors: 400, 401, 403, 404, 409, 429, 503 (Errors).

List organization findings

GET /findings · operationId listFindings · Scopes: findings:read · MCP tool list_findings

Findings across every registered repository at its default branch head, newest first.

ParameterInTypeRequiredNotes
statusquery("open" | "investigating" | "fixed" | "wont-fix" | "false-positive" | "duplicate")[]noat most 6 items
severityquery("info" | "low" | "medium" | "high" | "critical")[]noat most 5 items
assigneequerystringno"me", "none", a member id, a member email, or a member username. A username several members share matches all of them; 1–320 characters
focusquerystringnoFocus slug; 1–200 characters
dismissedquerybooleannotrue lists only dismissed findings; false (default) hides them; default false
limitqueryintegerno1–100; default 50
cursorquerystringno`nextCursor` from the previous page; 1–2048 characters

Returns 200 with nextCursor, items, counts, total, asOf, unavailableRepositories. Errors: 400, 401, 403, 404, 409, 429, 503 (Errors).

Change findings

PATCH /findings · operationId updateFindings · Scopes: findings:write · MCP tool update_findings

Apply one change to up to 100 findings: status (with reason), severity, assignee, or dismissal (with reason). open, fixed and false-positive are recorded per commit, at the default branch head unless repository/at say otherwise. Findings already in the requested state are skipped.

ParameterInTypeRequiredNotes
repositoryquerystring | integernoNeeded with `at` when a finding spans several repositories
atquerystringnoRepository position: "default", "branch:<name>[@<sha>]", "pr:<number>[@<sha>]" or "commit:<sha>". Omitted means "default"; at most 512 characters
Body fieldTypeRequiredNotes
findingsstring[]yes1–100 items
status"open" | "investigating" | "fixed" | "wont-fix" | "false-positive"noopen, fixed and false-positive apply per commit; the others to the finding
reasonstringnoRequired with status and with dismissed: true; 1–5000 characters
severity"info" | "low" | "medium" | "high" | "critical"no
assigneeinteger | string | nullnoMember id, member email, member username, "me", or null to unassign. A username several members share is refused; use the id or email
dismissedbooleanno

Returns 200 with findings, asOf. Errors: 400, 401, 403, 404, 409, 413, 415, 429, 503 (Errors).

Get a finding

GET /findings/{finding} · operationId getFinding · Scopes: findings:read · MCP tool get_finding

One finding with its reports, analysis, evidence, activity and comments. With at, code state and status are read at that commit.

ParameterInTypeRequiredNotes
findingpathstringyesFinding id, or its display key such as "F-42"; 1–255 characters
repositoryquerystring | integernoRepository: "owner/name" or its numeric id
atquerystringnoRepository position: "default", "branch:<name>[@<sha>]", "pr:<number>[@<sha>]" or "commit:<sha>". Omitted means "default"; at most 512 characters

Returns 200 with id, key, title, severity, status, investigating, decision, duplicateOf, dismissed, assignee, focus, firstObservedAt, reportCount, sources, runs, repositories, webUrl, description, reports, evidence, activity, related, asOf. Errors: 400, 401, 403, 404, 409, 429, 503 (Errors).

Change a finding

PATCH /findings/{finding} · operationId updateFinding · Scopes: findings:write · REST only

Apply one change to a finding: status (with reason), severity, assignee, or dismissal. A change that would not alter the finding is skipped.

ParameterInTypeRequiredNotes
findingpathstringyesFinding id, or its display key such as "F-42"; 1–255 characters
repositoryquerystring | integernoNeeded with `at` when a finding spans several repositories
atquerystringnoRepository position: "default", "branch:<name>[@<sha>]", "pr:<number>[@<sha>]" or "commit:<sha>". Omitted means "default"; at most 512 characters
Body fieldTypeRequiredNotes
status"open" | "investigating" | "fixed" | "wont-fix" | "false-positive"noopen, fixed and false-positive apply per commit; the others to the finding
reasonstringnoRequired with status and with dismissed: true; 1–5000 characters
severity"info" | "low" | "medium" | "high" | "critical"no
assigneeinteger | string | nullnoMember id, member email, member username, "me", or null to unassign. A username several members share is refused; use the id or email
dismissedbooleanno

Returns 200 with findings, asOf. Errors: 400, 401, 403, 404, 409, 413, 415, 429, 503 (Errors).

Comment on a finding

POST /findings/{finding}/comments · operationId commentOnFinding · Scopes: findings:write · MCP tool comment_on_finding

Add a comment, or a reply to one, to a finding.

ParameterInTypeRequiredNotes
findingpathstringyesFinding id, or its display key such as "F-42"; 1–255 characters
Idempotency-KeyheaderstringnoRepeat a request safely: the same key and body return the original result; 1–255 characters
Body fieldTypeRequiredNotes
bodystringyes1–20000 characters
replyTostringnoComment id to reply to; 1–255 characters

Returns 201 with comment. Errors: 400, 401, 403, 404, 409, 413, 415, 429, 503 (Errors).

Runs

OperationEndpointScopesMCP tool
List runsGET /runsruns:readlist_runs
Start a runPOST /runsruns:writestart_run
Get a runGET /runs/{run}runs:readget_run
List a run's findingsGET /runs/{run}/findingsruns:read, findings:readlist_run_findings
Estimate a runPOST /runs/estimateruns:writeestimate_run
Cancel a runPOST /runs/{run}/cancelruns:managecancel_run

List runs

GET /runs · operationId listRuns · Scopes: runs:read · MCP tool list_runs

Runs of the organization, newest first.

ParameterInTypeRequiredNotes
repositoryquerystring | integernoRepository: "owner/name" or its numeric id
focusquerystringnoFocus slug; 1–200 characters
statequery("queued" | "in_progress" | "compilation_in_progress" | "compilation_completed" | "compilation_failed" | "analysis_in_progress" | "analysis_completed" | "analysis_failed" | "complete" | "failed" | "cancelled")[]noat most 11 items
limitqueryintegerno1–100; default 50
cursorquerystringno`nextCursor` from the previous page; 1–2048 characters

Returns 200 with nextCursor, items. Errors: 400, 401, 403, 404, 409, 429, 503 (Errors).

Start a run

POST /runs · operationId startRun · Scopes: runs:write · MCP tool start_run

Start a paid run. Send the quote from estimate_run and a maxPriceCents the user approved; the run is refused when its price exceeds it. A repeated Idempotency-Key (REST) or requestId (MCP) returns the run it started.

ParameterInTypeRequiredNotes
Idempotency-KeyheaderstringnoRepeat a request safely: the same key and body return the original result; 1–255 characters
Body fieldTypeRequiredNotes
repositorystring | integeryesRepository: "owner/name" or its numeric id
atstringnoFull review position (default "default"). Mutually exclusive with change; at most 512 characters
changeobjectnoReview a change: { pr } or { branch, since? }
focusesstring[]noFocuses whose documents steer the run; at most 5 items
scopestring[]noat most 200 items
repositoriesobject[]noExtra repositories reviewed alongside; full reviews only; at most 8 items
steeringstringnoInstructions for the reviewers; at most 65536 characters
documentsstring[]no
maxPriceCentsintegeryesHighest price, in cents, the caller accepts; at least 0
quotestringno1–4096 characters

Returns 200, 201 with run, priceCents. Errors: 400, 401, 402, 403, 404, 409, 413, 415, 429, 503 (Errors).

Get a run

GET /runs/{run} · operationId getRun · Scopes: runs:read · MCP tool get_run

One run with its stages, progress, cost and finding import state. A failed or partial import says why in import.reason and on the inbox stage.

ParameterInTypeRequiredNotes
runpathinteger | stringyes

Returns 200 with id, title, state, phase, terminal, createdAt, startedAt, endedAt, repository, ref, sha, extraRepositories, retryOf, trigger, costCents, findings, error, webUrl, kind, cancellationRequested, focuses, change, repositories, stage, stages, import, publicUrl, pullRequestCommentUrl. Errors: 400, 401, 403, 404, 409, 429, 503 (Errors).

List a run's findings

GET /runs/{run}/findings · operationId listRunFindings · Scopes: runs:read, findings:read · MCP tool list_run_findings

Findings a completed run added to the inbox. Empty until import.state is "imported" or "partial"; a partial import lists only the findings that reached the inbox, and import.reason says why the rest did not.

ParameterInTypeRequiredNotes
runpathinteger | stringyes
limitqueryintegerno1–100; default 50
cursorquerystringno`nextCursor` from the previous page; 1–2048 characters

Returns 200 with nextCursor, items, import, asOf. Errors: 400, 401, 403, 404, 409, 429, 503 (Errors).

Estimate a run

POST /runs/estimate · operationId estimateRun · Scopes: runs:write · MCP tool estimate_run

Price a full review (at) or a change review (change) without starting it. Returns a quote to pass to start_run within 15 minutes.

Body fieldTypeRequiredNotes
repositorystring | integeryesRepository: "owner/name" or its numeric id
atstringnoFull review position (default "default"). Mutually exclusive with change; at most 512 characters
changeobjectnoReview a change: { pr } or { branch, since? }
focusesstring[]noFocuses whose documents steer the run; at most 5 items
scopestring[]noat most 200 items
repositoriesobject[]noExtra repositories reviewed alongside; full reviews only; at most 8 items
steeringstringnoInstructions for the reviewers; at most 65536 characters
documentsstring[]no

Returns 200 with kind, priceCents, balanceCents, targets, focusRevisions, change, quote, expiresAt. Errors: 400, 401, 403, 404, 409, 413, 415, 429, 503 (Errors).

Cancel a run

POST /runs/{run}/cancel · operationId cancelRun · Scopes: runs:manage · MCP tool cancel_run

Cancel a queued or running run. A run that has already started stops at its next checkpoint (result "cancelling").

ParameterInTypeRequiredNotes
runpathinteger | stringyes

Returns 200 with run, result. Errors: 400, 401, 403, 404, 409, 413, 415, 429, 503 (Errors).

Documents

OperationEndpointScopesMCP tool
Request a document upload slotPOST /documents/uploadsruns:writerequest_document_upload
Create a context documentPOST /documentsruns:writecreate_document
List context documentsGET /documentsruns:readlist_documents
Get a context documentGET /documents/{document}runs:readREST only
Archive a context documentDELETE /documents/{document}runs:writearchive_document

Request a document upload slot

POST /documents/uploads · operationId requestDocumentUpload · Scopes: runs:write · MCP tool request_document_upload

Get a URL to PUT a file to (up to 50 MiB, valid 10 minutes), then call create_document with the slot id.

Body fieldTypeRequiredNotes
filenamestringyes1–255 characters
contentTypestringyesMIME type of the file; 1–255 characters

Returns 201 with upload. Errors: 400, 401, 403, 404, 409, 413, 415, 429, 503 (Errors).

Create a context document

POST /documents · operationId createDocument · Scopes: runs:write · MCP tool create_document

Add a context document runs can read: an uploaded file ({ upload }) or a text note ({ note }). Identical content returns the existing document.

Body fieldTypeRequiredNotes
uploadobjectno
noteobjectno

Returns 201 with document. Errors: 400, 401, 403, 404, 409, 413, 415, 429, 503 (Errors).

List context documents

GET /documents · operationId listDocuments · Scopes: runs:read · MCP tool list_documents

Context documents of the organization, optionally those used with a repository.

ParameterInTypeRequiredNotes
repositoryquerystring | integernoRepository: "owner/name" or its numeric id
archivedquerybooleannodefault false
limitqueryintegerno1–100; default 50
cursorquerystringno`nextCursor` from the previous page; 1–2048 characters

Returns 200 with nextCursor, items. Errors: 400, 401, 403, 404, 409, 429, 503 (Errors).

Get a context document

GET /documents/{document} · operationId getDocument · Scopes: runs:read · REST only

One context document by id.

ParameterInTypeRequiredNotes
documentpathstringyesContext document id

Returns 200 with document. Errors: 400, 401, 403, 404, 409, 429, 503 (Errors).

Archive a context document

DELETE /documents/{document} · operationId archiveDocument · Scopes: runs:write · MCP tool archive_document

Archive a document so new runs stop using it. Only its creator or an owner may archive it.

ParameterInTypeRequiredNotes
documentpathstringyesContext document id

Returns 200 with document. Errors: 400, 401, 403, 404, 409, 429, 503 (Errors).

On this page