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 31 operations; MCP offers 28 of them as tools.

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.
  • 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 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.

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

Every error has the same JSON body, with a requestId matching the X-Request-Id header. See the error reference.

Per-user request budgets appear in X-RateLimit-* headers on every response. See Rate limits.

/api/v1 is retired and answers 410 api_version_retired. Use /api/v2 and CLI version 1.0.

On this page