API overview

REST, MCP and the CLI over one API: what they cover and the rules they share.

V12's API has three ways in: REST for scripts and services, an MCP server for AI assistants, and the v12 command-line tool. All three cover findings, runs and their guest links, context documents, repositories, Steers and members. REST offers all 35 operations. MCP offers 32 of them as tools, plus send_feedback, which has no REST endpoint: 33 tools in all.

Authentication

Send Authorization: Bearer <token> on every request, with an API key or OAuth access token. See Authentication and permissions.

Addresses

A token acts on one workspace, an organization or your personal workspace. Every REST endpoint except GET /api/v2/me sits under https://v12.sh/api/v2/orgs/<org>, where <org> is that workspace's slug. Short paths in these docs, such as POST /runs, omit that prefix.

  • Get the slug from organization.slug in GET https://v12.sh/api/v2/me, open to any valid token, or the app's address bar: v12.sh/<org>/….
  • Another workspace's slug gets 404 not_found, even for an organization you belong to. So does a path without /orgs/<org>.
  • A slug from before the workspace was renamed still works.
  • MCP takes no slug: a connection acts on its token's workspace.

Address things as the app shows them:

  • Finding: key F-42 or number 42. In a JSON body or an MCP call the number may be an integer, {"finding": 42}.
  • Run: number in the workspace, 12.
  • Repository: slug or full name owner/name, acme/api. A repository V12 hosts goes by its slug, v12/api.
  • Assignee: me, none, or a member's email or username.
  • Steer: slug, auth.
  • Context document: id, a UUID.
  • Guest link: number within its run, from 1. PATCH /runs/12/links/2 changes link 2 of run 12.

Responses use the same addresses, and findings, runs, repositories and Steers carry a webUrl that opens them in the app.

Positions

Operations that read code state take an optional at, default when left out:

atPosition
defaultHead of the default branch V12 uses for the repository
branch:<name>Head of a branch
pr:<number>Head of a pull request
commit:<sha>A commit, by its full 40-character SHA

Responses report the evaluated commit in position.at, pinned to its SHA: branch:main@<sha>. Send it back to stay on that commit after the branch moves.

An unresolvable position gets 409 position_unavailable, with details.reason such as unknown-branch or unknown-pull-request.

Pagination

Lists, except refs, return items and nextCursor. For the next page, repeat the request with the same filters and cursor set to nextCursor, until it is null. A cursor sent with different filters gets 400 invalid_request.

Where a list takes limit, it sets the page size: 1 to 100, default 50.

The refs list, GET /repositories/{repository}/refs (MCP list_refs), is the exception. It returns branches, pullRequests and tags arrays, not items, and takes no cursor: nextCursor is always null. Its limit is 1 to 200, default 100, and applies to each array; truncated says which may hold more, so narrow the list with q.

Safe retries

Run starts and comments take an idempotency key, Idempotency-Key in REST or requestId in MCP. A retry with the same key and request returns the original result. A replayed start adds replayed: true, shows the run as it was when it started, and doesn't count toward the run-start limit. Reusing a key for a different request gets 409 idempotency_key_reused. POST /runs needs a key or an estimate's quote. The CLI sends a key for you.

Finding changes and repository adds are safe to repeat without a key: they leave findings already in the requested state unchanged and list them in skipped, and return an existing repository with added: false.

Errors and limits

REST errors and MCP errors have the same JSON body, with a requestId matching the X-Request-Id header. See the error reference. The exception is an MCP protocol error, such as an unknown tool name or malformed JSON-RPC: it is a JSON-RPC error object that sets X-Request-Id but has no requestId. See Results and errors.

Request budgets appear in X-RateLimit-* headers on the responses to requests that were charged. Preflights, the OpenAPI document and the retired /api/v1 carry none. See Rate limits.

/api/v1 is retired and answers 410 api_version_retired. Use /api/v2 and CLI 1.0 or later; v12 repos create and v12 repos push need 1.2.

On this page