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,fixedandfalse-positiveare recorded at a position: the head of the default branch, unless you passrepositoryandat(--repoand--atin the CLI).investigatingandwont-fixapply 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 --watchThe 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.detailshaspriceCentsandmaxPriceCents.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:mainThe 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.