Built to be driven by other programs.
JSON contract
Every command has two output modes: human (default in TTY) and JSON (automatic when stdout is not a TTY, or when
--json is passed). JSON output is a stable envelope — agents can parse it with
jq without worrying about version drift.
{
"data": {
"id": "chat_8kwbm",
"object": "chat",
"latestVersion": {
"id": "ver_14lmv",
"files": [{ "name": "app/page.tsx" }]
}
}
}
# Errors use the same envelope shape
{
"error": {
"code": "rate_limited",
"type": "rate_limit_error",
"message": "Daily limit hit",
"command": "v0 chat create",
"auditId": "..."
}
}
Use --fields id,latestVersion.id to narrow the response and save context window in agent pipelines.
Streaming render
Generations take 30–60s. Humans see a live clack spinner with phase labels pulled from the SSE stream
(“Writing TicTacToe component…”, “Updating globals…”, etc). Agents get the same data as one NDJSON frame per line
when they pass --json --stream.
# Default in human TTY mode — clack spinner with phase labels
v0 "build a tic tac toe"
# Force streaming + JSON → NDJSON on stdout
v0 chat create --message "..." --stream --json
# Disable streaming in human mode (fallback for CI, NO_COLOR, or piped)
v0 chat create --message "..." --no-stream
SSE has no resume. A network flap requires re-sending the message. Prefer --background for long work.
Parallel chats
Serializing v0 generations wastes time. --background detaches a worker, returns the
chat_id in under a second, and persists state at
$APP_HOME/pending/<chat_id>.json. Reattach from any terminal.
# Each returns in <1s
v0 "hero section" --background --json
# → { "chat_id": "chat_abc", "status": "running", "pid": 12345 }
v0 "pricing table" --background --json
v0 "footer" --background --json
# Do other work. Then join.
v0 chat pending --json # list all in-flight
v0 chat wait chat_abc --json # block until ready → final envelope
v0 chat wait chat_def --timeout 60 # bounded wait; exit 124 on timeout
v0 chat watch chat_ghi # live stream render (human or --json)
v0 chat status chat_jkl --json # one-shot snapshot
v0 chat pending --clean # GC entries >1h oldchat waitblocks until the worker setsstatus: doneorfailed. Returns the same envelope as a synchronouschat create.chat watchtails the NDJSON stream log. Same human render in TTY, raw frames in--json.chat statusis a single-shot snapshot. Detects stalled workers viaisProcessAlive(pid).chat pending --cleanGCs finished entries older than 1h.
Params vs sugar flags
Every write command accepts two ways to pass the body: sugar flags like
--message and --model, OR a raw JSON blob via
--params. When both are set, --params wins. Humans should prefer the sugar; agents
should prefer --params to avoid building conflicting combinations.
v0 chat create --params '{
"message": "dashboard",
"chatPrivacy": "private",
"modelConfiguration": { "modelId": "v0-pro", "thinking": true }
}' --json
The same contract applies to chat init. Agents can pass a positional source for the
ergonomic form (v0 chat init ./my-app --json) or go fully explicit with
--params '{"type":"files","source":"./my-app"}' --json. Both are equally auditable —
the audit trail records the resolved params either way.