Skip to content

Veo Native CLI Passthrough ​

VideoClaw, the videoclaw mascot, illustrating veo cli

Drive Google Veo through your Google Flow account without leaving vclaw — check batches, resume paused jobs, upscale clips to 4K, and turn videos into GIFs, all from one command surface.

What it does ​

  • Bridges the vclaw veo * subcommands to the Bun-based vclaw-cli/flow.ts, which signs in to Google Flow in a real browser and calls useapi.net for the account-level verbs.
  • Standard batch verbs: status, list, history, resume, reset, cancel — inspect and steer Veo jobs that are already running.
  • UseAPI verbs: manage accounts, register CAPTCHA providers, check account health, generate and upscale images, upscale videos, and convert video to GIF.
  • Keeps the underlying tool's colon-separated form (e.g. useapi:accounts list) so existing scripts keep working.
  • The same Veo route also powers full pipeline runs via the native in-process transport (native-veo.ts), which drives flow.ts under the hood when you produce/execute a project on the veo-useapi route.

How to use it ​

All examples use node dist/cli/vclaw.js video ...; once the bin is on your PATH you can shorten this to vclaw .... The veo family lives directly under vclaw veo (not vclaw video).

bash
node dist/cli/vclaw.js veo status

Shows the status of the current Veo batch (pass a batchId to target a specific one).

bash
node dist/cli/vclaw.js veo list

Lists every Veo batch the CLI knows about.

bash
node dist/cli/vclaw.js veo history --limit 20

Prints recent job history, capped to the last N entries.

bash
node dist/cli/vclaw.js veo resume

Resumes a paused batch (optionally pass a batchId).

bash
node dist/cli/vclaw.js veo reset

Resets failed jobs back to pending so they can be retried.

bash
node dist/cli/vclaw.js veo cancel

Cancels the current batch.

bash
node dist/cli/vclaw.js veo useapi:accounts list

Lists your registered useapi.net accounts (use useapi:accounts add to register one).

bash
node dist/cli/vclaw.js veo useapi:health

Reports account health and history so you can confirm the route is live before spending credits.

bash
node dist/cli/vclaw.js veo useapi:captcha --provider 2captcha --key <key>

Registers a CAPTCHA-solving provider (or run useapi:captcha list to see what's configured).

bash
node dist/cli/vclaw.js veo useapi:image --image-prompt "a neon-lit alley at dusk"

Generates still images through useapi.net.

bash
node dist/cli/vclaw.js veo useapi:image:upscale --media-id <id> --resolution 4k

Upscales a generated image to 2k or 4k.

bash
node dist/cli/vclaw.js veo useapi:upscale --media-id <id> --resolution 4k

Upscales a generated video to 1080p or 4k.

bash
node dist/cli/vclaw.js veo useapi:gif --media-id <id> --output-file ./out.gif

Converts a video into a GIF — this one is free.

How it flows ​

veo cli diagram

Diagram source (live Mermaid)

Artifacts & outputs ​

  • Generated videos land in the Veo CLI output directory (vclaw-cli/output-videos/ by default, overridable with VCLAW_VEO_OUTPUT_DIR).
  • The native transport writes per-job state to <output-videos>/.vclaw-jobs/<externalJobId>.json (route, output dir, timestamps, output file list) so polling can resolve completed clips.
  • GIFs, upscaled images, and upscaled videos write to the path you pass (--output-file) or the useapi-returned media.
  • When the Veo route is used inside a project pipeline, the resulting clips and the execution-report flow into the standard projects/<slug>/artifacts/ layout. See Execution.

Tips & gotchas ​

Real human faces are rejected by content moderation (image-to-video)

Veo refuses to animate a photograph of a real person on this route. Measured 4 Sep 2026: four image-to-video scenes built from real photographs of a dental practice. The one keyframe with no person in it rendered on the first attempt; all three containing a person were refused, each with the byte-identical masked failure Generation failed: All operations failed.

Read the evidence rather than the message. isIpProhibitedFailure returned false, so this is the moderation class, not the intellectual-property one — which matters, because the two have opposite remedies. Clinical or anatomical imagery is not the variable: the refused set includes a receptionist holding a telephone with nothing anatomical in frame. Softening the motion wording (VCLAW_VEO_SAFE_MOTION=1) cannot clear a refusal about the input image.

It is bracketed, not inferred. Every refusal in that first run came after the single success, which "a face is present" and "something changed account-side mid-session" would fit equally well. Settled 5 Sep 2026 on veo_3_1_i2v_lite_low_priority at 0 credits: 00:38 no person → PASS, 00:39 real face → REFUSE, 00:42 no person → PASS. The person-free scene passes on both sides of the refusal, so the account was healthy throughout and the refusal is pinned to the image. Run the bracket, not the pair — a pass/refuse pair alone cannot separate the two explanations.

No cheaper tier clears it. That bracket ran on the lite row; the original refusals were the quality row (veo_3_1_i2v_s_portrait). Two different Veo model rows refuse the same three keyframes and pass the fourth.

Refusals are not charged — the account balance was unchanged across all of them. A failed attempt costs time, not credits.

omni-flash is not the way round it either. buildPrompt emits image:<path> (First-Frame I2V) only when the task carries firstFrame, which comes from a firstFrame field on the storyboard scene — and no CLI flag writes it. Without it the transport takes the ingredients: branch: with --veo-model omni-flash set, the payload quotes as abra_r2v_8s, operation r2v, not abra_i2v_8s. Confirmed against the price quote, not just the code.

R2V — whether reached this way or through vclaw video flow-register-characters — synthesises a scene from an identity reference rather than animating the photograph you supplied. Where the photograph itself is the deliverable (documentary, client-owned footage, a real named person), that is a fabrication decision, not a technical one, and belongs to whoever owns the material.

If the ad needs a real, named person on screen and you have their photograph, do not reach for an AI i2v route on Veo. A deterministic camera move on the still delivers the shot with zero refusal risk and zero identity drift, and that is what shipped.

A note on tiers that cuts the other way: on the one scene that did render — a static room, no people — the free lite tier produced a better take than the 100-credit quality tier: a clean one-way push where quality pushed in and then pulled back out. Both 1080x1920, 24fps, 8s, no letterbox. A static-room shot is not a case for the expensive tier.

Asset paths must be ABSOLUTE on this route

native-veo.ts passes the reference to flow.ts inside the prompt text as image:<path>, and runs it with cwd set to the vclaw-cli root — not the project directory. A project-relative path in the asset manifest therefore cannot resolve, and the run dies immediately with:

Generation failed: Image file not found: references/scene0-keyframe.png

It fails before submission, so it costs nothing — but the message names the file, not the cause, and the path looks perfectly correct in the manifest. Relative paths work on seedance-direct only because that route uploads the bytes instead of handing over a path, so a manifest that renders fine there is silently unusable here.

Fix: bind with absolute paths — vclaw video assets --project <slug> --asset "image:/abs/path/scene0-keyframe.png:0". The spec parser takes them verbatim, and gen-image already emits absolute paths in its own registerHint.

A partial run leaves execute-status blind

The transport renders scenes sequentially in one call and throws on the first scene that produces no output — before writeJobState. So when scene 1 fails, scene 0's clip has already been collected to outputs/scene-0.mp4, but outputs/.vclaw-jobs/ is empty, execute-status lists nothing, and no actualCost/priceRows were recorded for the clip that did land and was charged.

The clip is real and usable (assemble --from-clips reads outputs/ directly). Just do not treat the artifacts as the spend record after a partial run — the account balance delta is. Read it with veo useapi:health, before and after.

The same throw aborts every later scene, so re-run the survivors with --scene <i> rather than a bare produce, which would re-render (and re-charge) the scenes that already landed.

Bun is required

The bridge runs bun run flow.ts, so install Bun first: curl -fsSL https://bun.sh/install | bash. Point VCLAW_VEO_CLI_ROOT at your vclaw-cli directory if it isn't a sibling of the workspace, and override the binary with VCLAW_VEO_BUN_BIN if needed.

Stale Google Flow cookies cause timeouts

If a command times out or fails with a "session refresh failed" message, your Google Flow cookies have expired. Refresh them in the Veo CLI's cookie.json (setup guide: useapi.net setup-google-flow) and retry. The default per-command timeout is 180s (VCLAW_VEO_COMMAND_TIMEOUT_MS).

Keep the colon form

veo verbs use colon separators (useapi:accounts list, not useapi accounts list) to match the underlying Bun CLI. The legacy bun run vclaw-cli/flow.ts <verb> form still works in v3.0 but is deprecated — prefer vclaw veo *.

Driving it from an agent ​

These are thin passthrough commands: a non-zero exit means the underlying flow.ts call failed (commonly a session-refresh / cookie error, surfaced verbatim in stderr). Agents should treat any "session refresh failed" string as a cookie-refresh signal and not retry blindly. Run vclaw veo useapi:health as a cheap pre-flight before submitting paid work, and discover the canonical command list with vclaw schema --json. For full project runs on this route, see Providers and Execution.

Built to be driven by agent hosts like Claude Code, Claude Desktop, or Codex · Source-available, commercial use requires a paid license.