Recipes

Triage the inbox, start a run within a price limit, and push code to a repository V12 hosts, with the CLI, REST or an MCP assistant.

Three common jobs, with the v12 CLI, REST and an MCP assistant. The REST examples read an API key from V12_API_TOKEN and the workspace slug from V12_ORG; the API quickstart shows how to get both.

Triage the inbox

v12 findings list --status open --severity critical,high
v12 findings status fixed F-12 F-15 --reason "Fixed in #482"
v12 findings assign me F-12
v12 findings comment F-12 --body "The fix is in #482."

Inside a Git clone, the CLI takes the repository from origin, so findings list shows that repository's inbox. Pass --repo owner/name to pick another repository, or --org to list every repository.

The same rules apply everywhere:

  • Each request makes one kind of change: a status, a severity, an assignee, or a dismissal.
  • A status change and a dismissal both need a reason.
  • Findings already in the requested state are skipped, so repeating a change is safe.
  • open, fixed and false-positive are recorded at a position: the head of the default branch, unless you pass repository and at (--repo and --at in the CLI). investigating and wont-fix apply to the finding everywhere. See Status.

Start a run within a price limit

The repository must be added to the workspace. If it is not, the estimate answers 404 repository_not_added: add it with v12 repos add acme/api, POST /repositories, or the MCP tool add_repository, then estimate again. Adding a repository is free and does not start a run.

v12 runs start --repo acme/api --at branch:main --max-price 25 --watch

The command estimates the run and prints the price, asks you to confirm, then starts the run with the quote and a maximum of $25. --max-price is in dollars; --max-price-cents takes cents. Without either, the quoted price is the maximum. --watch follows the run until it ends, then lists its findings.

In CI, add --yes. Without a terminal to confirm in, the command otherwise stops with exit code 2.

Errors to expect, all listed in the error reference:

  • price_exceeds_max: the price is above your maximum. details has priceCents and maxPriceCents.
  • quote_stale: the quote expired, or what it priced changed. Estimate again.
  • insufficient_credits: the workspace needs more credit; see Add credit.
  • spend_cap_reached: the workspace hit its monthly limit.

The CLI exits with code 5 on each of these.

Push code to a hosted repository

V12 can host a Git repository for code that does not live on GitHub. Only workspace owners can create one or push to it, and their token needs repos:write.

# 1. Create an empty repository. It is addressed as v12/<name> from then on.
curl -s -X POST "https://v12.sh/api/v2/orgs/$V12_ORG/repositories/hosted" \
  -H "Authorization: Bearer $V12_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name":"payments-api"}'

# 2. Get a push URL for one branch. It works for one hour.
PUSH_URL=$(curl -s -X POST \
  "https://v12.sh/api/v2/orgs/$V12_ORG/repositories/v12/payments-api/push-credentials" \
  -H "Authorization: Bearer $V12_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"branch":"main"}' | jq -r .url)

# 3. Push.
git push "$PUSH_URL" HEAD:main

The create answers 201 with the repository; a name already in use answers 409 conflict. The push URL answers 201 with url, branch and expiresAt. The URL embeds its own credential: keep it out of logs and shell history, and do not save it as a remote. It pushes only to the branch you named; ask for another URL to push another branch, or after it expires.

V12 imports every push, so the repository's branches appear in GET /repositories/v12/payments-api/refs once the import finishes, and runs can review it like any other repository.

On this page