REST reference
Every /api/v2 endpoint: method, path, scopes, parameters, body fields and errors.
The REST API lives under https://v12.sh/, where <org> is the slug of the workspace your token reads. Only GET https://v12.sh/ and the OpenAPI document sit outside it. Addresses explains slugs and how to name findings, runs and repositories. 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/.
None required in an operation's scopes means any valid token can call it, as with GET /me; it does not remove the token requirement.
Requests
- Paths below are relative to
https://v12.sh/api/v2.{org}isorganization.slugfromGET /me; another workspace's slug gets 404not_found. {repository}is two path segments,owner/name: its slug or full name, as in/orgs/.acme/ repositories/ acme/ api/ findings {finding}is a finding key, such asF-42, or its number,42.{run}is the run's number in the workspace.{link}is a guest link's number in that run.repositoryandatgo in the query string on everyGETthat takes them, and on the two findingPATCHoperations. A comment'sPOSTtakes them in its JSON body.- Query lists are comma-separated:
status=open,fixed. A repeated list parameter adds its values, sostatus=open&status=fixedmeans the same; repeating a single-value parameter, such aslimit, returns 400invalid_request. - Other
POSTandPATCHfields go in a JSON body withContent-Type: application/. Any other type returnsjson 415. - Bodies are limited to 1 MiB. A larger one returns
413.
Errors
Every error response has this body. details is optional. 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"
}| Code | HTTP | Meaning |
|---|---|---|
invalid_request | 400 | The input, a header, the cursor or the quote is invalid. Validation errors list details.issues. |
repository_required | 400 | The finding is in several repositories. Pick one from details.repositories. |
unauthenticated | 401 | The Authorization header is missing or its token is invalid. See WWW-Authenticate. |
insufficient_credits | 402 | The workspace lacks the credit for the run. |
spend_cap_reached | 402 | The workspace's monthly spending limit is reached. |
insufficient_scope | 403 | The token lacks a scope listed in details.required. |
forbidden | 403 | The token's user has a role that does not allow the action, such as a Member adding a private repository. |
not_found | 404 | No such endpoint (such as a path without /orgs/<org>), a slug that is not the token's workspace, no such resource in the token's workspace, or the finding is not at that position. A finding that is on no default branch, sent without at, has details.reason not-on-default-branch: pass repository and at. |
repository_not_visible | 404 | V12 can't see the repository on GitHub, so it can't add it. details.known is private only when your own GitHub account sees it as private. A token an OAuth app holds, including v12 auth login's, always gets unknown. details.recovery holds a next step you may take, such as giving V12's GitHub App access, or null. |
repository_not_added | 404 | A run target is not added to the workspace. Add it with POST /repositories, then estimate again. details.add names the operation and the repos:write scope it needs; granted says whether this token holds it. When it doesn't, add the repository in the app, or use a token with repos:write. |
method_not_allowed | 405 | The path does not take this method. See Allow. |
position_unavailable | 409 | V12 cannot resolve the position. details.reason says why, such as unknown-branch (with details.ref, a branch name without refs/heads/; a hosted repository that has no commit on its default branch yet answers it too, and its message says to push that branch first), unknown-pull-request (with details.number) or repository-empty for a GitHub repository without commits. A finding read that V12's findings service can't place at a repository position also gives details.mirror, the repository's sync state; git-data-unavailable means V12 has no commit to read at the position yet, such as while it is still mirroring the repository. |
decision_active | 409 | A won't-fix or risk-accepted decision blocks this status. details.findings lists the findings; set status open first. |
conflict | 409 | The request conflicts with the current state, such as cancelling a completed run. |
price_exceeds_max | 409 | The price, details.priceCents, is above maxPriceCents. |
quote_stale | 409 | The quote expired or what it priced changed. Estimate again. |
change_refused | 409 | The change cannot be reviewed. details.reason is thin, identical, large or detached, and the message says what to send instead. large means more than 3,000 changed files, removed ones included. |
nothing_to_review | 409 | Nothing in scope has files to review at this position. |
repository_not_runnable | 409 | Access to the repository was revoked; details.reason is access-revoked. |
idempotency_key_reused | 409 | The Idempotency-Key was used for a different request. |
api_version_retired | 410 | /api/v1 is retired. Use /api/v2. |
payload_too_large | 413 | The body is larger than details.limitBytes. |
unsupported_media_type | 415 | The body is not application/json. |
rate_limited | 429 | A rate limit is used up. Wait Retry-After seconds; details.bucket names the limit and details.limit its size. With details.source: "github", GitHub is limiting V12's requests; try again in a few minutes. |
internal_error | 500 | An unexpected failure on V12's side, including a 500 from V12's findings service. |
upstream_error | 503 | A request V12 needed failed: to GitHub, or to V12's repository mirror, or a hosted repository's setup hasn't finished. Try again shortly. |
platform_unavailable | 503 | V12's findings service or repository mirror is unavailable: the findings service answered 429, 502, 503 or 504, or didn't answer, or the mirror couldn't answer in time. When the service said when to retry, the response carries Retry-After, with the same seconds in details.retryAfterSeconds; wait that long. |
repository_access_unavailable | 503 | V12 could not confirm its GitHub access to the repository. With details.reason: "public-reader-unavailable", V12 cannot read public repositories for this workspace until GitHub is connected. |
rate_limit_unavailable | 503 | Rate limiting is down, so V12 refuses changes and run estimates. Retry after 30 seconds. |
public_api_disabled | 503 | The API is turned off for now; the web app still works. |
Me
| Operation | Endpoint | Scopes | MCP tool |
|---|---|---|---|
| Get the token identity | GET /me | None required | get_me |
Get the token identity
GET /me · operationId getMe · Scopes: None required · MCP tool get_me
The user, organization and scopes behind this credential, plus the organization credit balance when the user may view billing. Any valid token may read it; organization.slug addresses every other endpoint.
Returns 200 with user, organization, token, credits. Errors: 400, 401, 403, 404, 405, 409, 429, 500, 503 (Errors).
Members
| Operation | Endpoint | Scopes | MCP tool |
|---|---|---|---|
| List organization members | GET /orgs/{org}/members | user:read | list_members |
List organization members
GET /orgs/{org}/members · operationId listMembers · Scopes: user:read · MCP tool list_members
Members of the token organization, sorted by username, then email. Use a member email or username to assign findings.
| Parameter | In | Type | Required | Notes |
|---|---|---|---|---|
org | path | string | yes | Organization slug; must be the token's organization |
Returns 200 with nextCursor, items. Errors: 400, 401, 403, 404, 405, 409, 429, 500, 503 (Errors).
Steers
| Operation | Endpoint | Scopes | MCP tool |
|---|---|---|---|
| List Steers | GET /orgs/{org}/steers | repos:read | list_steers |
| Create a Steer | POST /orgs/{org}/steers | steers:write | create_steer |
| Get a Steer | GET /orgs/ | repos:read | get_steer |
| Edit a Steer | PATCH /orgs/ | steers:write | update_steer |
| Archive a Steer | DELETE /orgs/ | steers:write | archive_steer |
| Restore a Steer | POST /orgs/ | steers:write | restore_steer |
List Steers
GET /orgs/{org}/steers · operationId listSteers · Scopes: repos:read · MCP tool list_steers
Organization-wide Steer documents that guide runs and Autopilot rules. Lists active Steers by default; archived: true lists only archived Steers.
| Parameter | In | Type | Required | Notes |
|---|---|---|---|---|
org | path | string | yes | Organization slug; must be the token's organization |
archived | query | boolean | no | default false |
Returns 200 with nextCursor, items. Errors: 400, 401, 403, 404, 405, 409, 429, 500, 503 (Errors).
Create a Steer
POST /orgs/{org}/steers · operationId createSteer · Scopes: steers:write · MCP tool create_steer
Create an organization Steer with a title, emoji and instructions. The returned slug is its stable address. Repeating this request creates another Steer; it does not attach to Autopilot rules.
| Parameter | In | Type | Required | Notes |
|---|---|---|---|---|
org | path | string | yes | Organization slug; must be the token's organization |
| Body field | Type | Required | Notes |
|---|---|---|---|
title | string | yes | 1–120 characters |
emoji | string | yes | at least 1 characters |
instructions | string | yes | at most 65536 characters |
Returns 201 with slug, title, emoji, archivedAt, webUrl, instructions. Errors: 400, 401, 403, 404, 405, 409, 413, 415, 429, 500, 503 (Errors).
Get a Steer
GET /orgs/ · operationId getSteer · Scopes: repos:read · MCP tool get_steer
One active or archived Steer by slug, including its instructions and archive status. Archived Steers remain readable for historical references.
| Parameter | In | Type | Required | Notes |
|---|---|---|---|---|
org | path | string | yes | Organization slug; must be the token's organization |
steer | path | string | yes | Steer slug; 1–200 characters |
Returns 200 with slug, title, emoji, archivedAt, webUrl, instructions. Errors: 400, 401, 403, 404, 405, 409, 429, 500, 503 (Errors).
Edit a Steer
PATCH /orgs/ · operationId updateSteer · Scopes: steers:write · MCP tool update_steer
Update the supplied fields of an active Steer, preserving omitted fields and its slug. Runs already started keep the instructions they started with. Restore an archived Steer before editing it.
| Parameter | In | Type | Required | Notes |
|---|---|---|---|---|
org | path | string | yes | Organization slug; must be the token's organization |
steer | path | string | yes | Steer slug; 1–200 characters |
| Body field | Type | Required | Notes |
|---|---|---|---|
title | string | no | 1–120 characters |
emoji | string | no | at least 1 characters |
instructions | string | no | at most 65536 characters |
Returns 200 with slug, title, emoji, archivedAt, webUrl, instructions. Errors: 400, 401, 403, 404, 405, 409, 413, 415, 429, 500, 503 (Errors).
Archive a Steer
DELETE /orgs/ · operationId archiveSteer · Scopes: steers:write · MCP tool archive_steer
Archive a Steer and detach it from every Autopilot rule. Rules keep their active or paused state; a rule with no remaining Steers uses its default review. New runs refuse the Steer, while finding tags and queued, running and completed run snapshots remain intact. Repeating archive is safe.
| Parameter | In | Type | Required | Notes |
|---|---|---|---|---|
org | path | string | yes | Organization slug; must be the token's organization |
steer | path | string | yes | Steer slug; 1–200 characters |
Returns 200 with slug, title, emoji, archivedAt, webUrl, instructions. Errors: 400, 401, 403, 404, 405, 409, 429, 500, 503 (Errors).
Restore a Steer
POST /orgs/ · operationId restoreSteer · Scopes: steers:write · MCP tool restore_steer
Make an archived Steer available for edits and new runs again, keeping its slug. Former Autopilot attachments are not restored; add them again separately. Repeating restore is safe.
| Parameter | In | Type | Required | Notes |
|---|---|---|---|---|
org | path | string | yes | Organization slug; must be the token's organization |
steer | path | string | yes | Steer slug; 1–200 characters |
Returns 200 with slug, title, emoji, archivedAt, webUrl, instructions. Errors: 400, 401, 403, 404, 405, 409, 413, 415, 429, 500, 503 (Errors).
Repositories
| Operation | Endpoint | Scopes | MCP tool |
|---|---|---|---|
| List repositories | GET /orgs/ | repos:read | list_repositories |
| Add a repository | POST /orgs/ | repos:write | add_repository |
| Get a repository | GET /orgs/ | repos:read | REST only |
| List branches, pull requests and tags | GET /orgs/ | repos:read | list_refs |
| Create a hosted repository | POST /orgs/ | repos:write | create_hosted_repository |
| Get a push URL for a hosted repository | POST /orgs/ | repos:write | create_push_credential |
List repositories
GET /orgs/ · 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.
| Parameter | In | Type | Required | Notes |
|---|---|---|---|---|
org | path | string | yes | Organization slug; must be the token's organization |
Returns 200 with nextCursor, items. Errors: 400, 401, 403, 404, 405, 409, 429, 500, 503 (Errors).
Add a repository
POST /orgs/ · operationId addRepository · Scopes: repos:write · MCP tool add_repository
Add a GitHub repository to this organization. Members can add public repositories; private repositories must be granted by the workspace GitHub connection and added by an owner. Repeating an add returns the existing registration. A "v12/<name>" names a repository V12 hosts, never one on GitHub: it returns that registration, or not_found. This does not start a run.
| Parameter | In | Type | Required | Notes |
|---|---|---|---|---|
org | path | string | yes | Organization slug; must be the token's organization |
| Body field | Type | Required | Notes |
|---|---|---|---|
repository | string | yes | Repository: "owner/name", its github.com URL, or its clone URL; 1–300 characters |
Returns 200, 201 with added, tracking, repository. Errors: 400, 401, 403, 404, 405, 409, 413, 415, 429, 500, 503 (Errors).
Get a repository
GET /orgs/ · operationId getRepository · Scopes: repos:read · REST only
One registered repository by its slug or full name ("owner/name").
| Parameter | In | Type | Required | Notes |
|---|---|---|---|---|
org | path | string | yes | Organization slug; must be the token's organization |
owner | path | string | yes | Repository owner |
repo | path | string | yes | Repository name |
Returns 200 with slug, fullName, defaultBranch, access, mirror, webUrl. Errors: 400, 401, 403, 404, 405, 409, 429, 500, 503 (Errors).
List branches, pull requests and tags
GET /orgs/ · operationId listRefs · Scopes: repos:read · MCP tool list_refs
Branches, pull requests and tags V12 has mirrored for a repository, with whether each head was reviewed. Each list stops at limit (default 100, max 200): truncated says which may hold more and branchCount counts every matching branch. Not paginated; narrow with q. mirror is the repository's sync state.
| Parameter | In | Type | Required | Notes |
|---|---|---|---|---|
org | path | string | yes | Organization slug; must be the token's organization |
owner | path | string | yes | Repository owner |
repo | path | string | yes | Repository name |
q | query | string | no | Case-insensitive name filter; at most 200 characters |
limit | query | integer | no | 1–200; default 100 |
Returns 200 with defaultBranch, branches, pullRequests, tags, asOf, branchCount, truncated, mirror, nextCursor. Errors: 400, 401, 403, 404, 405, 409, 429, 500, 503 (Errors).
Create a hosted repository
POST /orgs/ · operationId createHostedRepository · Scopes: repos:write · MCP tool create_hosted_repository
Create an empty Git repository that V12 hosts. It answers to "v12/<name>", and to the slug in the response. Push code to it with a URL from create_push_credential, the default branch first: V12 does not enforce that, but until it exists, finding reads and full reviews at the default position answer 409 position_unavailable. V12 imports each push. Only owners may create one. A name already in use, or whose "v12/<name>" is another repository's slug, is a conflict. This does not start a run.
| Parameter | In | Type | Required | Notes |
|---|---|---|---|---|
org | path | string | yes | Organization slug; must be the token's organization |
| Body field | Type | Required | Notes |
|---|---|---|---|
name | string | yes | Name: 1 to 100 lowercase letters, numbers, dots, underscores or hyphens, starting and ending with a letter or number, e.g. "payments-api"; 1–100 characters |
Returns 201 with repository. Errors: 400, 401, 403, 404, 405, 409, 413, 415, 429, 500, 503 (Errors).
Get a push URL for a hosted repository
POST /orgs/ · operationId createPushCredential · Scopes: repos:write · MCP tool create_push_credential
A Git remote URL that can push to one branch of a repository V12 hosts, valid for one hour. Push with `git -c credential.helper= push <url> HEAD:<branch>`: the empty helper keeps Git from saving the credential. The URL is the credential; treat it as a secret. Only owners may get one, and GitHub repositories have none. V12 does not make you push the default branch first, but until it exists, finding reads and full reviews at the default position, and branch changes without since, answer 409 position_unavailable.
| Parameter | In | Type | Required | Notes |
|---|---|---|---|---|
org | path | string | yes | Organization slug; must be the token's organization |
owner | path | string | yes | Repository owner |
repo | path | string | yes | Repository name |
| Body field | Type | Required | Notes |
|---|---|---|---|
branch | string | yes | The one branch the URL may push to, e.g. "main": a name Git accepts. "refs/heads/main" names main; other refs, such as "refs/tags/x", are refused; 1–255 characters |
Returns 201 with branch, url, expiresAt. Errors: 400, 401, 403, 404, 405, 409, 413, 415, 429, 500, 503 (Errors).
Findings
| Operation | Endpoint | Scopes | MCP tool |
|---|---|---|---|
| List a repository's findings at a position | GET /orgs/ | findings:read | list_repository_findings |
| List organization findings | GET /orgs/{org}/findings | findings:read | list_findings |
| Change findings | PATCH /orgs/ | findings:write | update_findings |
| Get a finding | GET /orgs/ | findings:read | get_finding |
| Change a finding | PATCH /orgs/ | findings:write | REST only |
| Comment on a finding | POST /orgs/ | findings:write | comment_on_finding |
List a repository's findings at a position
GET /orgs/ · 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. Highest finding number first; counts are before filters, total after.
| Parameter | In | Type | Required | Notes |
|---|---|---|---|---|
org | path | string | yes | Organization slug; must be the token's organization |
owner | path | string | yes | Repository owner |
repo | path | string | yes | Repository name |
at | query | string | no | Repository position: "default", "branch:<name>[@<sha>]", "pr:<number>[@<sha>]" or "commit:<sha>". Omitted means "default"; at most 512 characters |
status | query | ("open" | "investigating" | "fixed" | "wont-fix" | "false-positive" | "duplicate")[] | no | at most 6 items |
severity | query | ("info" | "low" | "medium" | "high" | "critical")[] | no | at most 5 items |
assignee | query | string | no | "me", "none", a member email, or a member username. A username several members share matches all of them; 1–320 characters |
steer | query | string | no | Steer slug; 1–200 characters |
limit | query | integer | no | 1–100; default 50 |
cursor | query | string | no | `nextCursor` from the previous page; 1–2048 characters |
Returns 200 with nextCursor, items, repository, position, reviewCoverage, counts, total, asOf. Errors: 400, 401, 403, 404, 405, 409, 429, 500, 503 (Errors).
List organization findings
GET /orgs/{org}/findings · operationId listFindings · Scopes: findings:read · MCP tool list_findings
Findings across every registered repository at its default branch head, highest finding number first (numbers grow as findings are created; firstObservedAt can be older). counts covers every finding before filters; total counts the filtered result. unavailableRepositories lists the slugs of repositories whose default branch V12 cannot read; list_repository_findings on one says why.
| Parameter | In | Type | Required | Notes |
|---|---|---|---|---|
org | path | string | yes | Organization slug; must be the token's organization |
status | query | ("open" | "investigating" | "fixed" | "wont-fix" | "false-positive" | "duplicate")[] | no | at most 6 items |
severity | query | ("info" | "low" | "medium" | "high" | "critical")[] | no | at most 5 items |
assignee | query | string | no | "me", "none", a member email, or a member username. A username several members share matches all of them; 1–320 characters |
steer | query | string | no | Steer slug; 1–200 characters |
limit | query | integer | no | 1–100; default 50 |
cursor | query | string | no | `nextCursor` from the previous page; 1–2048 characters |
Returns 200 with nextCursor, items, counts, total, asOf, unavailableRepositories. Errors: 400, 401, 403, 404, 405, 409, 429, 500, 503 (Errors).
Change findings
PATCH /orgs/ · operationId updateFindings · Scopes: findings:write · MCP tool update_findings
Apply one change to up to 100 findings: status, severity, or assignee. fixed and open are recorded at a commit (the default branch head unless repository/at say otherwise) and carry forward to later commits while the affected files are unchanged; false-positive marks the finding's reports and shows at every commit that has them; investigating and wont-fix apply everywhere. repository and at also locate findings that exist only off the default branch, such as ones a change review reported (send each position.at that list_run_findings reports with its repository): for investigating, wont-fix, severity and assignee they only locate the findings, and the change still applies everywhere. Findings already in the requested state are left unchanged and listed in skipped.
| Parameter | In | Type | Required | Notes |
|---|---|---|---|---|
org | path | string | yes | Organization slug; must be the token's organization |
repository | query | string | no | Repository that `at` refers to. Needed with `at` when a finding spans several repositories |
at | query | string | no | Where to find the findings: "default", "branch:<name>[@<sha>]", "pr:<number>[@<sha>]" or "commit:<sha>", such as a `position.at` a finding reports; omitted means "default". open, fixed and false-positive are recorded at this position. For every other change it only locates a finding that exists there, such as one found only off the default branch, and the change still applies everywhere; at most 512 characters |
| Body field | Type | Required | Notes |
|---|---|---|---|
findings | (integer | string)[] | yes | 1–100 items |
status | "open" | "investigating" | "fixed" | "wont-fix" | "false-positive" | no | fixed and open apply at a commit and carry forward; false-positive marks the finding's reports; investigating and wont-fix apply everywhere |
severity | "info" | "low" | "medium" | "high" | "critical" | no | |
assignee | string | null | no | "me", a member email, a member username, or null or "none" to unassign. A username several members share is refused; use the email |
Returns 200 with findings, skipped, asOf. Errors: 400, 401, 403, 404, 405, 409, 413, 415, 429, 500, 503 (Errors).
Get a finding
GET /orgs/ · operationId getFinding · Scopes: findings:read · MCP tool get_finding
One finding with its reports (each report's analysis, proof of concept and fix are in reports[].analysis), evidence and activity. Comments are activity entries of kind "comment"; their id is the replyTo for comment_on_finding. A false-positive change shows in status, not in activity. With at, code state and status are read at that commit. Send a returned position.at with its repositories[i].repository; without repository, inference reports repository_required if several registered repositories hold the finding at that position. A finding on no default branch answers not_found unless you send at.
| Parameter | In | Type | Required | Notes |
|---|---|---|---|---|
org | path | string | yes | Organization slug; must be the token's organization |
finding | path | integer | string | yes | Finding key such as "F-42", or its number |
repository | query | string | no | Repository slug or full name, "owner/name" |
at | query | string | no | Repository position: "default", "branch:<name>[@<sha>]", "pr:<number>[@<sha>]" or "commit:<sha>". Omitted means "default"; at most 512 characters |
Returns 200 with number, key, title, severity, status, investigating, decision, duplicateOf, assignee, steer, firstObservedAt, reportCount, sources, runs, repositories, webUrl, description, reports, evidence, activity, related, asOf. Errors: 400, 401, 403, 404, 405, 409, 429, 500, 503 (Errors).
Change a finding
PATCH /orgs/ · operationId updateFinding · Scopes: findings:write · REST only
Apply one change to a finding: status, severity, or assignee. repository and at locate a finding that exists only off the default branch, such as one a change review reported: for investigating, wont-fix, severity and assignee they only locate it, and the change still applies everywhere. A change that would not alter the finding leaves it unchanged and lists it in skipped.
| Parameter | In | Type | Required | Notes |
|---|---|---|---|---|
org | path | string | yes | Organization slug; must be the token's organization |
finding | path | integer | string | yes | Finding key such as "F-42", or its number |
repository | query | string | no | Repository that `at` refers to. Needed with `at` when a finding spans several repositories |
at | query | string | no | Where to find the findings: "default", "branch:<name>[@<sha>]", "pr:<number>[@<sha>]" or "commit:<sha>", such as a `position.at` a finding reports; omitted means "default". open, fixed and false-positive are recorded at this position. For every other change it only locates a finding that exists there, such as one found only off the default branch, and the change still applies everywhere; at most 512 characters |
| Body field | Type | Required | Notes |
|---|---|---|---|
status | "open" | "investigating" | "fixed" | "wont-fix" | "false-positive" | no | fixed and open apply at a commit and carry forward; false-positive marks the finding's reports; investigating and wont-fix apply everywhere |
severity | "info" | "low" | "medium" | "high" | "critical" | no | |
assignee | string | null | no | "me", a member email, a member username, or null or "none" to unassign. A username several members share is refused; use the email |
Returns 200 with findings, skipped, asOf. Errors: 400, 401, 403, 404, 405, 409, 413, 415, 429, 500, 503 (Errors).
Comment on a finding
POST /orgs/ · operationId commentOnFinding · Scopes: findings:write · MCP tool comment_on_finding
Add a comment, or a reply to one, to a finding. A finding that exists only off the default branch, such as one a change review reported, needs repository and at (a position.at that list_run_findings reports); the comment belongs to the finding everywhere.
| Parameter | In | Type | Required | Notes |
|---|---|---|---|---|
org | path | string | yes | Organization slug; must be the token's organization |
finding | path | integer | string | yes | Finding key such as "F-42", or its number |
Idempotency-Key | header | string | no | Repeat a request safely: the same key and body return the original result; 1–255 characters |
| Body field | Type | Required | Notes |
|---|---|---|---|
body | string | yes | 1–20000 characters |
replyTo | string | no | Comment id to reply to; 1–255 characters |
repository | string | no | Repository that `at` refers to. Needed with `at` when a finding spans several repositories |
at | string | no | Where to find the finding: "default", "branch:<name>[@<sha>]", "pr:<number>[@<sha>]" or "commit:<sha>", such as a `position.at` a finding reports; omitted means "default". Needed for a finding that exists only off the default branch; at most 512 characters |
Returns 201 with comment. Errors: 400, 401, 403, 404, 405, 409, 413, 415, 429, 500, 503 (Errors).
Runs
| Operation | Endpoint | Scopes | MCP tool |
|---|---|---|---|
| List runs | GET /orgs/{org}/runs | runs:read | list_runs |
| Start a run | POST /orgs/{org}/runs | runs:write | start_run |
| Get a run | GET /orgs/ | runs:read | get_run |
| List a run's findings | GET /orgs/ | runs:read, findings:read | list_run_findings |
| Estimate a run | POST /orgs/ | runs:write | estimate_run |
| Cancel a run | POST /orgs/ | runs:manage | cancel_run |
| List guest links | GET /orgs/ | runs:share | list_run_links |
| Create a guest link | POST /orgs/ | runs:share, findings:read | create_run_link |
| Change a guest link | PATCH /orgs/ | runs:share, findings:read | update_run_link |
| Revoke a guest link | DELETE /orgs/ | runs:share | revoke_run_link |
List runs
GET /orgs/{org}/runs · operationId listRuns · Scopes: runs:read · MCP tool list_runs
Runs of the organization, newest first. state filters raw states (failed alone misses compilation_failed and analysis_failed); phase filters the phase each run reports. With phase, a page can hold fewer than limit items: keep following nextCursor.
| Parameter | In | Type | Required | Notes |
|---|---|---|---|---|
org | path | string | yes | Organization slug; must be the token's organization |
repository | query | string | no | Repository slug or full name, "owner/name" |
steer | query | string | no | Steer slug; 1–200 characters |
state | query | ("queued" | "in_progress" | "compilation_in_progress" | "compilation_completed" | "compilation_failed" | "analysis_in_progress" | "analysis_completed" | "analysis_failed" | "complete" | "failed" | "cancelled")[] | no | 1–11 items |
phase | query | ("before" | "live" | "complete" | "failed" | "cancelled")[] | no | 1–5 items |
limit | query | integer | no | 1–100; default 50 |
cursor | query | string | no | `nextCursor` from the previous page; 1–2048 characters |
Returns 200 with nextCursor, items. Errors: 400, 401, 403, 404, 405, 409, 429, 500, 503 (Errors).
Start a run
POST /orgs/{org}/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 original response with replayed: true. A replay, or a start refused before billing (an invalid or expired quote, its repository, a price above maxPriceCents, its documents), spends nothing from the start limit; a start that reaches billing counts even when it is refused there, as when its quoted change or commit has moved (quote_stale). Without a quote, the estimate the start makes counts toward the estimate limit.
| Parameter | In | Type | Required | Notes |
|---|---|---|---|---|
org | path | string | yes | Organization slug; must be the token's organization |
Idempotency-Key | header | string | no | Repeat a request safely: the same key and body return the original result; 1–255 characters |
| Body field | Type | Required | Notes |
|---|---|---|---|
repository | string | yes | Repository slug or full name, "owner/name" |
at | string | no | Full review position (default "default"). Mutually exclusive with change; at most 512 characters |
change | object | no | Review a change: { pr } (GitHub repositories only) or { branch, since? } (GitHub and hosted repositories) |
steers | string[] | no | Saved Steers whose documents guide the run; at most 5 items |
scope | string[] | no | Folders (ending in /) and files to review, relative to the repository root. Required for a full review: V12 reviews only what it names, and refuses a path that names no file it reviews at the position; at most 200 items |
repositories | object[] | no | Extra repositories reviewed alongside; full reviews only; at most 8 items |
steering | string | no | Instructions for the reviewers; at most 65536 characters |
documents | string[] | no | |
maxPriceCents | integer | yes | Highest price, in cents, the caller accepts; at least 0 |
quote | string | no | 1–4096 characters |
Returns 200, 201 with run, priceCents, replayed. Errors: 400, 401, 402, 403, 404, 405, 409, 413, 415, 429, 500, 503 (Errors).
Get a run
GET /orgs/ · 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. import.count counts the run's reports; a report can join an existing finding, whose status (for example a won't-fix decision) then applies.
| Parameter | In | Type | Required | Notes |
|---|---|---|---|---|
org | path | string | yes | Organization slug; must be the token's organization |
run | path | integer | string | yes | Run number within the organization, e.g. 12 |
Returns 200 with number, title, state, phase, terminal, createdAt, startedAt, endedAt, source, repository, ref, sha, extraRepositories, retryOf, trigger, costCents, findings, error, webUrl, kind, cancellationRequested, steers, change, repositories, stage, stages, import, publicUrl, pullRequestCommentUrl. Errors: 400, 401, 403, 404, 405, 409, 429, 500, 503 (Errors).
List a run's findings
GET /orgs/ · operationId listRunFindings · Scopes: runs:read, findings:read · MCP tool list_run_findings
Findings a completed run added to the inbox, positioned at each repository's analyzed commit; send position.at together with its repositories[i].repository for positioned reads. Without repository, inference reports repository_required if several registered repositories hold the finding at that position. Status and codeState describe that commit, not today's default head. 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. A finding can predate the run: runs lists every run that reported it.
| Parameter | In | Type | Required | Notes |
|---|---|---|---|---|
org | path | string | yes | Organization slug; must be the token's organization |
run | path | integer | string | yes | Run number within the organization, e.g. 12 |
limit | query | integer | no | 1–100; default 50 |
cursor | query | string | no | `nextCursor` from the previous page; 1–2048 characters |
Returns 200 with nextCursor, items, import, asOf. Errors: 400, 401, 403, 404, 405, 409, 429, 500, 503 (Errors).
Estimate a run
POST /orgs/ · 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. targets[].paths (or change.paths) lists up to 500 in-scope paths.
| Parameter | In | Type | Required | Notes |
|---|---|---|---|---|
org | path | string | yes | Organization slug; must be the token's organization |
| Body field | Type | Required | Notes |
|---|---|---|---|
repository | string | yes | Repository slug or full name, "owner/name" |
at | string | no | Full review position (default "default"). Mutually exclusive with change; at most 512 characters |
change | object | no | Review a change: { pr } (GitHub repositories only) or { branch, since? } (GitHub and hosted repositories) |
steers | string[] | no | Saved Steers whose documents guide the run; at most 5 items |
scope | string[] | no | Folders (ending in /) and files to review, relative to the repository root. Required for a full review: V12 reviews only what it names, and refuses a path that names no file it reviews at the position; at most 200 items |
repositories | object[] | no | Extra repositories reviewed alongside; full reviews only; at most 8 items |
steering | string | no | Instructions for the reviewers; at most 65536 characters |
documents | string[] | no |
Returns 200 with kind, priceCents, balanceCents, targets, change, quote, expiresAt. Errors: 400, 401, 403, 404, 405, 409, 413, 415, 429, 500, 503 (Errors).
Cancel a run
POST /orgs/ · 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").
| Parameter | In | Type | Required | Notes |
|---|---|---|---|---|
org | path | string | yes | Organization slug; must be the token's organization |
run | path | integer | string | yes | Run number within the organization, e.g. 12 |
Returns 200 with run, result. Errors: 400, 401, 403, 404, 405, 409, 413, 415, 429, 500, 503 (Errors).
List guest links
GET /orgs/ · operationId listRunLinks · Scopes: runs:share · MCP tool list_run_links
A run's guest links, newest first, with their number, role, state and guest count. A link's number addresses it within the run. The full URL is never listed; urlPrefix helps recognise a link.
| Parameter | In | Type | Required | Notes |
|---|---|---|---|---|
org | path | string | yes | Organization slug; must be the token's organization |
run | path | integer | string | yes | Run number within the organization, e.g. 12 |
Returns 200 with nextCursor, items. Errors: 400, 401, 403, 404, 405, 409, 429, 500, 503 (Errors).
Create a guest link
POST /orgs/ · operationId createRunLink · Scopes: runs:share, findings:read · MCP tool create_run_link
Create a link that opens this run to people outside the organization, without an account. Links are numbered 1, 2, 3… within the run, and a number is never reused. The API returns the full URL only in this response; owners can copy a live link again from the run's Share dialog. Works on any run but a draft, including one still running or with no findings.
| Parameter | In | Type | Required | Notes |
|---|---|---|---|---|
org | path | string | yes | Organization slug; must be the token's organization |
run | path | integer | string | yes | Run number within the organization, e.g. 12 |
| Body field | Type | Required | Notes |
|---|---|---|---|
role | "view" | "comment" | "triage" | no | default "comment" |
expiresInDays | integer | no | Days from now until the link ends; 1–30; default 14 |
label | string | no | 1–80 characters |
Returns 201 with link, url. Errors: 400, 401, 403, 404, 405, 409, 413, 415, 429, 500, 503 (Errors).
Change a guest link
PATCH /orgs/ · operationId updateRunLink · Scopes: runs:share, findings:read · MCP tool update_run_link
Change a link's role, label or expiry (days from now), by run number and link number. A new expiry revives an expired link, within the live-link cap. A revoked link cannot change.
| Parameter | In | Type | Required | Notes |
|---|---|---|---|---|
org | path | string | yes | Organization slug; must be the token's organization |
run | path | integer | string | yes | Run number within the organization, e.g. 12 |
link | path | integer | string | yes | Link number within the run, e.g. 3 |
| Body field | Type | Required | Notes |
|---|---|---|---|
role | "view" | "comment" | "triage" | no | |
expiresInDays | integer | no | Days from now until the link ends; 1–30 |
label | string | null | no | null removes the label |
Returns 200 with link. Errors: 400, 401, 403, 404, 405, 409, 413, 415, 429, 500, 503 (Errors).
Revoke a guest link
DELETE /orgs/ · operationId revokeRunLink · Scopes: runs:share · MCP tool revoke_run_link
End a link now, by run number and link number; anyone using it loses access at their next request. Revoking twice returns the same link.
| Parameter | In | Type | Required | Notes |
|---|---|---|---|---|
org | path | string | yes | Organization slug; must be the token's organization |
run | path | integer | string | yes | Run number within the organization, e.g. 12 |
link | path | integer | string | yes | Link number within the run, e.g. 3 |
Returns 200 with link. Errors: 400, 401, 403, 404, 405, 409, 429, 500, 503 (Errors).
Documents
| Operation | Endpoint | Scopes | MCP tool |
|---|---|---|---|
| Request a document upload slot | POST /orgs/ | runs:write | request_document_upload |
| Create a context document | POST /orgs/ | runs:write | create_document |
| List context documents | GET /orgs/ | runs:read | list_documents |
| Get a context document | GET /orgs/ | runs:read | REST only |
| Archive a context document | DELETE /orgs/ | runs:write | archive_document |
Request a document upload slot
POST /orgs/ · 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. Types: PDF, Markdown, text, JSON, DOCX, CSV.
| Parameter | In | Type | Required | Notes |
|---|---|---|---|---|
org | path | string | yes | Organization slug; must be the token's organization |
| Body field | Type | Required | Notes |
|---|---|---|---|
filename | string | yes | 1–255 characters |
contentType | string | yes | MIME type of the file; 1–255 characters |
Returns 201 with upload. Errors: 400, 401, 403, 404, 405, 409, 413, 415, 429, 500, 503 (Errors).
Create a context document
POST /orgs/ · 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 with its original title (created: false); if that document was archived, this restores it (restored: true). An upload slot works once.
| Parameter | In | Type | Required | Notes |
|---|---|---|---|---|
org | path | string | yes | Organization slug; must be the token's organization |
| Body field | Type | Required | Notes |
|---|---|---|---|
upload | object | no | |
note | object | no |
Returns 201 with document, created, restored. Errors: 400, 401, 403, 404, 405, 409, 413, 415, 429, 500, 503 (Errors).
List context documents
GET /orgs/ · operationId listDocuments · Scopes: runs:read · MCP tool list_documents
Context documents of the organization, newest first. Archived documents are left out unless archived: true, which lists them beside the active ones. With repository, documents used by earlier runs on that repository come first and have recommended: true; it ranks, it does not filter.
| Parameter | In | Type | Required | Notes |
|---|---|---|---|---|
org | path | string | yes | Organization slug; must be the token's organization |
repository | query | string | no | Repository slug or full name, "owner/name" |
archived | query | boolean | no | True also lists archived documents, each marked archived: true; the default lists active documents only; default false |
limit | query | integer | no | 1–100; default 50 |
cursor | query | string | no | `nextCursor` from the previous page; 1–2048 characters |
Returns 200 with nextCursor, items. Errors: 400, 401, 403, 404, 405, 409, 429, 500, 503 (Errors).
Get a context document
GET /orgs/ · operationId getDocument · Scopes: runs:read · REST only
One context document by id.
| Parameter | In | Type | Required | Notes |
|---|---|---|---|---|
org | path | string | yes | Organization slug; must be the token's organization |
document | path | string | yes | Context document id |
Returns 200 with document. Errors: 400, 401, 403, 404, 405, 409, 429, 500, 503 (Errors).
Archive a context document
DELETE /orgs/ · operationId archiveDocument · Scopes: runs:write · MCP tool archive_document
Archive a document so new runs refuse it (estimate_run and start_run answer conflict). Creating identical content again restores it. Only its creator or an owner may archive it.
| Parameter | In | Type | Required | Notes |
|---|---|---|---|---|
org | path | string | yes | Organization slug; must be the token's organization |
document | path | string | yes | Context document id |
Returns 200 with document. Errors: 400, 401, 403, 404, 405, 409, 429, 500, 503 (Errors).