Skip to Content
AgentsCLICLI Reference

CLI Reference

CLI version 0.2.0. API version v1. Requires Node.js 20 or newer.

Commands are gavana GROUP ACTION [ARGUMENTS] [OPTIONS]. gavana-canvas, craftboard, and craftboard-canvas are the same program under different names.

Every group accepts --help, which prints usage for that group. gavana canvas --help and gavana node create --help both work.

How output works

One JSON object goes to stdout. Progress and errors go to stderr, so stdout stays parseable.

{ "ok": true, "result": {} }

Output formats

--outputProduces
json (default)The ok/result envelope on one line
jsonlOne JSON object per line; arrays are expanded into rows
markdownA chat-ready rendering, including image links for completed generation
humanA flattened, indented label: value view for terminal reading
rawBare strings and numbers, one per line, without JSON quotes

--pretty indents the JSON envelope by two spaces. -r / --raw is equivalent to --output raw and may only be combined with --output json or --output raw — pairing it with human, jsonl, or markdown is a usage error.

Selecting with --jq

--jq takes a jq-style path and is evaluated by a small built-in selector, so no jq installation is required.

gavana canvas list --limit 25 --jq '.canvases[].handle' -r gavana canvas get canvas:OWNER_UID:CANVAS_ID --jq '.canvas.revision' -r gavana asset list --jq '.assets[]' --output jsonl

Supported syntax: property paths (.canvas.revision), non-negative array indexes (.canvases[0].title), and [] projections (.connections[].models[]). Paths are limited to 500 characters, must start with ., and cannot select __proto__, prototype, or constructor. Anything else is a usage error.

--jq changes the shape of the output. Without it you get { "ok": true, "result": … }; with it you get only the selected value, unwrapped. Scripts that expect the envelope must not use --jq, and scripts that use --jq must not look for .result.

Errors

Errors are written to stderr:

{ "ok": false, "error": { "code": "input_validation_error", "message": "baseRevision is required.", "fields": [ { "field": "baseRevision", "message": "Read the canvas and pass its current revision." } ], "requestId": "7f8e0d9d-4bf8-4e6e-96e0-8bcd6a8f315a" } }

requestId is safe to share with support. Nothing else from a failing request is. Never share a token.

Server error codes map from HTTP status: 400/422 to validation, 401 to unauthorized, 403 to forbidden, 404 to not_found, 409 to conflict, 429 to rate_limited, 5xx to upstream. Locally raised errors use usage, configuration, network, timeout, canceled, invalid_response, or internal.

Retry behaviour

Transient network failures and 429, 502, 503, 504 responses are retried automatically — up to three attempts, respecting Retry-After — only for GET requests. No write is ever retried automatically, and no paid generation request is ever retried automatically.

Exit codes

ExitMeaning
0Success
1Any other failure, including a failed doctor
2Usage, configuration, or validation error; HTTP 400, 413, 415, 422
3Authentication or authorization error; HTTP 401, 403
4Revision or idempotency conflict; HTTP 409
5Not found; HTTP 404
7Network error — Gavana could not be reached
8Wait timeout — the Run or Job can still be resumed
9A waited Run or Job ended failed, canceled, or expired

Exit 9 applies to image generate|edit|variations, video generate, action run, recipe run, job wait, and run wait — the commands that wait for a terminal state. The complete terminal resource is still written to stdout, so a script can read the reason. Exit 8 means you stopped waiting, not that the work stopped: resume it with run wait or job wait.

Global options

OptionEffect
--output FORMAThuman, json, jsonl, markdown, or raw; JSON is the default
--prettyPretty-print JSON
--jq PATHSelect a jq-style path, for example .canvases[].handle
-r, --rawPrint selected strings and numbers without JSON quotes
--limit NMaximum list items to return
--cursor VALUEContinue a list from page.nextCursor
--base-url URLOverrides saved configuration
--profile NAMEUse one named account/environment profile
--token-stdinRead a login token from stdin
--base-revision VALUEEnforce a previously read canvas revision
--idempotency-key VALUEMake retries deterministic
--destination VALUEagent-canvas, new-canvas, or an existing canvas handle
--model VALUEProvider model id, or a model: handle from model list
--element VALUERepeatable exact element:<id>@v<n> revision for image generation
--connection VALUEDisambiguate a provider model id by connection
--input VALUERecipe KEY=VALUE or Action input; repeat as needed
--param NAME=VALUEAction parameter; repeat for multiple parameters
--webhook-url URLSend one signed callback when a Run finishes
--webhook-secret-env VARRead the signing secret from this variable (default GAVANA_WEBHOOK_SECRET)
--canvas-title VALUETitle used when --destination is new-canvas
--no-waitReturn immediately after queueing Recipe, image, video, or Action work
--progressWrite Run or video Job state transitions to stderr

Also available everywhere: --help / -h, --version / -v / -V, --yes for destructive confirmations, --timeout SECONDS and --interval SECONDS for waits, and --json / --file to supply a full JSON payload instead of individual flags.

Argument parsing

  • --flag value and --flag=value are both accepted.
  • Boolean flags take no value: help, version, pretty, raw, yes, wait, no-wait, progress, token-stdin, no-verify, no-browser, audio, no-audio, read-only.
  • Repeatable options: --reference, --element, --input, --param, --target, --ratio, --approved, --field. Any other repeated option keeps its last value.
  • -- ends option parsing; everything after it is positional.
  • A non-boolean flag whose next argument starts with -- is treated as true. If a value could start with --, use --flag=value.

Command groups

Twenty-three groups: action, ai-connection, api, asset, auth, campaign, canvas, capabilities, completion, config, connection, doctor, element, image, job, mcp, model, node, provider, recipe, run, version, video.


auth

Authenticate a profile.

gavana auth login [--profile NAME] [--read-only] [--no-browser] [--no-verify] gavana auth login --token-stdin [--profile NAME] [--base-url URL] gavana auth status [--profile NAME] gavana auth logout [--profile NAME]
OptionEffect
--read-onlyRequest only canvas:read, asset:read, and element:read at consent
--no-browserPrint the authorization URL instead of opening a browser
--token-stdinRead a Personal Access Token from stdin instead of using OAuth
--no-verifySkip the post-login verification call
--base-url URLSign in to a specific environment

Browser login. The CLI registers an OAuth client at /oauth/register, opens /oauth/authorize with PKCE (S256), listens on a loopback callback, and exchanges the code at /oauth/token for the resource /api/canvas-agent/v1. It times out after three minutes. If verification or config writing fails afterwards, the CLI revokes the token it just obtained rather than leaving a live credential behind.

Output. login returns authenticated, profile, baseUrl, configPath, a masked token, verified, loginMethod (browser or token), and — when verification ran — authType and scopes. status adds configured and, where readable, canvasCount. logout revokes an OAuth token at /oauth/revoke, removes the profile, and returns the remaining activeProfile.

Tokens are masked as the first seven characters, an ellipsis, and the last four.

gavana auth login --read-only gavana auth status --jq '.scopes[]' -r

Exit codes. 0; 2 for a usage error such as an unknown action; 3 if the credential is rejected; 7/8 for network or timeout.


config

Manage named profiles. Never contacts the network.

gavana config list gavana config get [PROFILE] gavana config use PROFILE

config with no action behaves as list; profiles is accepted as an alias for list.

Output. list returns activeProfile, configPath, and a sorted profiles array of name, baseUrl, active, and configured. get returns one profile’s profile, baseUrl, configured, masked token, and configPath. use returns active: true, the profile name, and configPath.

gavana config list --output human gavana config use work

Exit codes. 0; 2 for an unknown profile or an invalid profile name.


doctor

Diagnose the local setup.

gavana doctor [--profile NAME]

Runs four checks — node, credentials, base_url, authentication — and returns ok, profile, and the checks array. A failed authentication check includes the error code and requestId.

gavana doctor --output human gavana doctor --jq '.checks[]' --output jsonl

Exit codes. 0 when every check passes, 1 when any check fails.


api

Raw passthrough to the Canvas API. Paths are restricted to /api/canvas-agent/v1.

gavana api GET /canvases --field limit=10 gavana api POST /canvases --json '{"title":"Concepts"}' gavana api /canvases

The method is optional and defaults to GET. Valid methods are GET, POST, PUT, PATCH, DELETE, case-insensitive.

OptionEffect
--field NAME=VALUERepeatable. Query parameters on GET, request body fields otherwise
--json TEXTA full JSON body
--file PATHA JSON body read from a file, or - for stdin

Paths must match a conservative pattern and cannot contain ... --json and --file are mutually exclusive.

Output. The API response, unmodified, inside the standard envelope. When the server sent an X-Request-ID and the payload has no requestId of its own, the CLI adds it.

gavana api GET /canvases --field limit=5 --jq '.canvases[].handle' -r gavana api GET /auth/status --pretty

Exit codes. Mapped from the HTTP status: 2, 3, 4, 5, 7, 8, or 1.


mcp

Register or print an MCP client configuration.

gavana mcp install codex gavana mcp install claude gavana mcp config cursor|chatgpt|local

install executes the client’s own registration command. config prints the definition without changing anything. install supports only codex and claude; config accepts codex, claude, cursor, chatgpt, and local (stdio is an alias for local).

ClientDefinition
codexstreamable-http; runs codex mcp add gavana --url ENDPOINT
claudestreamable-http; runs claude mcp add --transport http gavana ENDPOINT
cursorstreamable-http; prints an mcpServers object with a url
chatgptstreamable-http; prints the endpoint and connector instructions
localstdio; npx -y @gavana.ai/mcp@0.2.0

The hosted endpoint is the profile’s base URL plus /mcp. For a local server, set GAVANA_MCP_READ_ONLY=true in the process environment if you want only non-mutating tools registered.

gavana mcp install codex gavana mcp config local --pretty

Exit codes. 0; 2 for an unsupported client or action; 1 if the client command fails.


capabilities

Print the machine-readable capability summary. Requires no credential.

gavana capabilities

Output. apiVersion, cliVersion, the ten toolsets (canvas, recipes, assets, elements, models, actions, images, videos, runs, campaigns), the 29 hosted remote capabilities each with toolset, readOnly, and paid flags, and the endpoints object (/mcp, /api/canvas-agent/v1).

gavana capabilities --jq '.remote[]' --output jsonl

Exit codes. 0.


version

gavana version gavana --version gavana -v

Output. { "name": "@gavana.ai/cli", "version": "0.2.0", "node": "v20.x.x" }.

Exit codes. 0.


completion

gavana completion zsh gavana completion bash gavana completion fish

Writes a completion script to stdout. With no shell argument it uses --shell, then the basename of $SHELL, then zsh.

gavana completion zsh >> ~/.zshrc

Exit codes. 0; 2 for an unsupported shell.


canvas

gavana canvas list [--limit N] [--cursor CURSOR] gavana canvas agent gavana canvas create --title "New canvas" [--id ID] gavana canvas get canvas:OWNER_UID:CANVAS_ID gavana canvas render canvas:CANVAS_ID [--file canvas.svg] gavana canvas apply canvas:CANVAS_ID --file batch.json [--base-revision REV] [--idempotency-key KEY] [--yes]
SubcommandNotes
list--limit is 1–25. Continue with --cursor from page.nextCursor
agentResolves or creates the persistent Agent Canvas
create--title defaults to the joined positional arguments, then to Untitled canvas. Accepts --json/--file instead
getReturns the full graph and the current revision
renderReturns SVG. With --file it writes the file and returns mediaType, file, and bytes; without, it returns mediaType and svg. --file - returns the SVG inline
applyApplies an atomic operation batch

canvas apply in detail. Operations come from --json, --file, or --operations as a JSON array. If the batch contains any node.delete or connection.delete, --yes is required. --base-revision is read from the canvas automatically when omitted; --idempotency-key is generated as a UUID when omitted. Supply both explicitly for anything you might need to retry.

gavana canvas list --limit 25 --jq '.canvases[].handle' -r gavana canvas get canvas:OWNER_UID:CANVAS_ID --jq '.canvas.revision' -r gavana canvas render canvas:OWNER_UID:CANVAS_ID --file ./board.svg gavana canvas apply canvas:OWNER_UID:CANVAS_ID --file ./batch.json --idempotency-key brief-2026-08-04-a

Exit codes. 0; 2 for a missing handle or a malformed batch; 4 on a revision conflict; 5 if the canvas does not exist.


node

Every node write is applied as a single-operation atomic batch, with the canvas’s current revision read automatically unless you pass --base-revision, and a generated UUID idempotency key unless you pass --idempotency-key.

gavana node get canvas:CANVAS_ID node:NODE_ID gavana node create canvas:CANVAS_ID --type text --title "Direction" --prompt "..." gavana node update canvas:CANVAS_ID node:NODE_ID --title "Approved" gavana node move canvas:CANVAS_ID node:NODE_ID --x 800 --y 120 gavana node resize canvas:CANVAS_ID node:NODE_ID --width 420 --height 300 gavana node delete canvas:CANVAS_ID node:NODE_ID --yes
SubcommandOptions
create--type (default text), --title (default Agent Node), --x --y (default 0), --width, --height, --content, --prompt, --metadata JSON, --client-id
update--title, --x and --y (both or neither), --width, --height, --content, --prompt, --metadata JSON
move--x and --y, both required
resize--width and --height, both required
delete--yes required

--content and --prompt are written into the node’s metadata. An update with no recognised option is a usage error rather than a no-op write. All of these accept --json/--file to supply the node or patch directly.

gavana node create canvas:OWNER_UID:CANVAS_ID \ --type text --title "Customer objections" \ --x 1200 --y 200 --width 1040 --height 720 \ --metadata '{"isSection":true}' gavana node create canvas:OWNER_UID:CANVAS_ID \ --type sticky --title "Observation" \ --content "Buyers hesitate at the price reveal" \ --x 1248 --y 300 gavana node delete canvas:OWNER_UID:CANVAS_ID node:NODE_ID --yes

Exit codes. 0; 2 for a missing handle, an empty patch, or a delete without --yes; 4 on a revision conflict; 5 if the canvas or node does not exist.


connection

gavana connection list canvas:CANVAS_ID gavana connection create canvas:CANVAS_ID --from node:NODE_ID --to node:NODE_ID [--mode MODE] gavana connection delete canvas:CANVAS_ID connection:CONNECTION_ID --yes

list reads the canvas and returns canvas, revision, and connections.

--mode values: omit it for a normal dependency or transformation flow, reference for an image reference guiding another node, list for a Prompt List or Image List flow, first-frame and last-frame for the frames of a video node. A first-frame or last-frame edge must connect an image node to a video node, and a video node may have only one of each. --from and --to can also be given as the second and third positional arguments, and --client-id names the new connection within a batch.

gavana connection list canvas:OWNER_UID:CANVAS_ID --jq '.connections[]' --output jsonl gavana connection create canvas:OWNER_UID:CANVAS_ID --from node:BRIEF --to node:GENERATOR gavana connection create canvas:OWNER_UID:CANVAS_ID --from node:STILL --to node:CLIP --mode first-frame gavana connection delete canvas:OWNER_UID:CANVAS_ID connection:CONNECTION_ID --yes

Exit codes. 0; 2 for a missing handle or a delete without --yes; 4 on a revision conflict; 5 if an endpoint does not exist.


asset

gavana asset list [--canvas canvas:CANVAS_ID] [--limit N] [--cursor CURSOR] gavana asset get asset:ASSET_ID gavana asset get asset:OWNER_UID:ASSET_ID gavana asset upload path/to/image.png

--limit on list is 1–200. The canvas filter may also be the first positional argument.

upload accepts a file path, --file PATH, - for stdin, or clipboard on macOS. The file must be a PNG, JPEG, WebP, or GIF — the CLI sniffs the magic bytes rather than trusting the extension — and must be at most 50 MB. Anything else is a usage error before a byte is sent.

Use the owner-qualified asset:OWNER_UID:ASSET_ID handle exactly as returned for a shared asset.

Scopes. list and get need asset:read. upload needs asset:read plus either image:generate or video:generate; when the upload is scoped to a canvas it also needs canvas:read and canvas:write.

gavana asset list --canvas canvas:OWNER_UID:CANVAS_ID --limit 50 --jq '.assets[].handle' -r gavana asset upload ./product.png --jq '.asset.handle' -r pbpaste | true; gavana asset upload clipboard

Exit codes. 0; 2 for an unsupported file, an oversized file, or a missing handle; 5 if the asset does not exist.


element

Elements are reusable visual references built from existing assets, written guidelines, or both. Current Elements use element:<id> handles; every immutable revision uses element:<id>@v<n>.

gavana element list [query] [--state active|archived] [--limit N] [--cursor CURSOR] gavana element get element:<id>@v<n> gavana element history element:<id> [--limit N] [--cursor CURSOR] gavana element create --name "Soft window light" --type lighting [--source-asset asset:<id>] [--guidelines "..."] gavana element update element:<id> --name "..." --type lighting [--source-asset asset:<id>] [--guidelines "..."] gavana element collections element:<id> [--collection element-collection:<id>] gavana element archive element:<id> [--yes] gavana element restore element:<id> gavana element collection-list [--limit N] [--cursor CURSOR] gavana element collection-create --name "Campaign assets" gavana element collection-rename element-collection:<id> --name "..." gavana element collection-delete element-collection:<id> [--yes]

create and update accept up to eight source assets, guidelines up to 2,000 characters, or both. Updating visual content creates a new immutable revision. Collection membership is deliberately separate: use element collections, which does not create a visual revision.

archive is recoverable and asks for confirmation interactively; scripts must pass --yes. Collection deletion has the same rule and never deletes the Elements inside it. The CLI exposes no permanent Element deletion.

Scopes. Read commands need element:read. Create, update, organize, archive, restore, and collection mutations need element:read plus element:write.

gavana element list "light" --jq '.elements[].versionHandle' -r gavana element get element:soft-window-light@v2 --pretty gavana element archive element:soft-window-light --yes

Exit codes. 0; 2 for an invalid or unpinned handle or a destructive non-interactive command without --yes; 3 for a missing scope; 5 when the Element or collection does not exist.


provider and ai-connection

Two names for the same command. Lists the AI provider connections saved on the account.

gavana provider list [--limit N] [--cursor CURSOR] gavana ai-connection list [--limit N] [--cursor CURSOR]

--limit is 1–100. Only list exists.

Scopes. Requires image:generate or video:generate — a purely read-only token cannot browse connections.

gavana provider list --output human

Exit codes. 0; 2 for an invalid --limit; 3 if the token has neither generation scope.


model

gavana model list [query] [--provider PROVIDER] [--capability CAPABILITY] [--limit N] [--cursor CURSOR] gavana model get model:OPAQUE_MODEL_KEY

--limit is 1–100. The query may be given as --query or as positional words. Useful capability filters include image.generate, image.edit, video.generate, video.generate.fromImage, video.generate.fromFrames, and video.generate.fromReferences.

model get returns the model’s exact parameters, valid option values, minimums and maximums, capabilities, connection, and estimated duration. Read it before starting paid work — it is how you find out which durations and aspect ratios a model actually accepts.

Scopes. Both subcommands require image:generate or video:generate. Browsing the model catalog is not available to a read-only token.

gavana model list --capability video.generate --jq '.models[].handle' -r gavana model get model:OPAQUE_MODEL_KEY --pretty

Exit codes. 0; 2 for an invalid --limit; 5 if the model handle does not exist.


action

Deterministic Image Actions make precise raster changes without asking an AI model and without spending AI credits. The first stable set is resize, crop, change aspect ratio, side-by-side composite, add text, overlay image, colour grade, and rotate.

gavana action list [query] [--limit N] [--cursor CURSOR] gavana action get action:SLUG gavana action run action:SLUG --input VALUE --destination DESTINATION [--param NAME=VALUE]

--limit on list is 1–100.

action run in detail. You must supply exactly as many --input values as the Action declares, in the order shown by action get — the CLI checks the count before sending anything. Inputs accept a node: or asset: handle, a local PNG/JPEG/WebP/GIF path, - for stdin, or clipboard on macOS. Local, stdin, and clipboard inputs are uploaded privately first, which is why they additionally require image:generate or video:generate on the token; existing handles do not.

Scopes. list and get need canvas:read. run needs canvas:read, canvas:write, and asset:read — no generation scope, because Actions are deterministic — plus a generation scope only when an input is a local file, stdin, or the clipboard, and job:manage to wait for the result.

OptionEffect
--destinationagent-canvas, new-canvas, or a canvas handle. Required
--canvas-titleRequired when --destination is new-canvas
--param NAME=VALUERepeatable schema parameter
--target node:NODE_IDWrite into an existing node instead of creating one
--titleTitle for the created target node; defaults to the Action title plus “result”
--no-waitReturn the queued run: handle immediately
--progressStream state transitions to stderr
--timeout SECONDSDefault 900 (15 minutes)
--interval SECONDSPoll interval, default 1.5
--webhook-url, --webhook-secret-envOne signed terminal callback

Declared parameters also get generated flags: a parameter id in camelCase becomes a kebab-case flag, so aspectRatio is --aspect-ratio. Numeric parameters are validated as numbers.

gavana action list gavana action get action:resize --pretty gavana action run action:resize \ --input ./product.png \ --destination agent-canvas \ --width 1080 --height 1350 gavana action run action:side-by-side-composite \ --input node:FIRST_IMAGE \ --input asset:SECOND_IMAGE \ --destination canvas:OWNER_UID:CANVAS_ID \ --direction horizontal --gap 24

Output. The terminal Run with typed outputs and durable node: and asset: handles, plus a destination object recording what you requested, the resolved canvas, and the target node ids.

Exit codes. 0; 2 for the wrong number of inputs, a missing destination, or an unsupported file; 3 if the token lacks a required scope; 8 if you stopped waiting; 9 if the Run ended failed, canceled, or expired.


recipe

Recipes are reusable canvas workflows. Forking and running are deliberately separate.

gavana recipe search [query] [--limit N] [--cursor CURSOR] gavana recipe get recipe:RECIPE_ID [--version VERSION] gavana recipe fork recipe:RECIPE_ID --canvas canvas:CANVAS_ID --idempotency-key KEY [--x X --y Y] gavana recipe run recipe:RECIPE_ID --input KEY=VALUE --destination DESTINATION

--limit on search is 1–100.

recipe fork adds a private, editable Recipe card to a canvas and never executes it. Options: --canvas (or the second positional argument), --version, --base-revision, --x, --y, --idempotency-key.

recipe run is the explicit execution step. It binds typed inputs, creates the declared output nodes on the destination canvas, and starts text or image work.

OptionEffect
--input KEY=VALUERepeatable. One per declared input
--destinationagent-canvas, new-canvas, or a canvas handle. Required
--canvas-titleRequired when --destination is new-canvas
--versionPin a Recipe version
--instance-node node:NODE_IDRun a connected private Recipe instance instead of supplying inputs
--model, --size, --qualityOverride generation settings
--no-wait, --progress, --timeout, --intervalWaiting behaviour; default timeout 900 seconds
--webhook-url, --webhook-secret-envOne signed terminal callback
--base-revision, --idempotency-keyConcurrency and retry control

Input keys. Use the exact keys recipe get returns. The CLI also accepts a port’s node id, its normalised label, and — where unambiguous — the first word of its label. Supplying the same input twice is an error.

Input values.

FormMeaning
plain textLiteral text
node:NODE_ID or asset:ASSET_IDAn existing handle
@path/to/fileOn a written port, read the file as text
@-Read text from stdin
@@literalAn escaped value that really starts with @
A path, -, or clipboard on an image portUploaded privately, then passed as an asset: handle

The @path distinction is the one that bites: on a written port @brief.md inserts the file’s text; on an image port the value is treated as an image reference. To use a local visual on a written port that accepts “an image or written note”, upload it first with asset upload and pass the returned asset: handle.

recipe run is paid work. Every Recipe start currently requires image:generate and its dependent scopes — including text-only Recipes. Ask for explicit approval in the current turn, and never retry a failed run automatically.

gavana recipe search "product visual" --jq '.recipes[].handle' -r gavana recipe get recipe:product-visual-direction --pretty gavana recipe fork recipe:product-visual-direction \ --canvas canvas:OWNER_UID:CANVAS_ID \ --idempotency-key fork-product-visual-a gavana recipe run recipe:product-visual-direction \ --input product-context="A matte black travel bottle for a quiet premium campaign" \ --destination canvas:OWNER_UID:CANVAS_ID \ --idempotency-key brief-2026-08-04-a \ --progress gavana recipe run recipe:social-creative-angles \ --input offer-brief=@brief.md \ --destination agent-canvas

Output. The terminal Run with typed outputs, durable handles, estimatedSeconds, observed queue and execution timing, and — on failure — a stable failure.code and failure.retryable. With --no-wait, the queued Run plus a destination object.

Exit codes. 0; 2 for a missing input, an unknown input key, or a missing destination; 3 for a missing scope; 4 on a conflict; 8 if you stopped waiting; 9 on a terminal failed, canceled, or expired.


image

gavana image generate --destination DESTINATION --prompt "..." gavana image edit --destination DESTINATION --reference REFERENCE --prompt "..." gavana image variations --destination DESTINATION --reference REFERENCE --prompt "..."
OptionEffect
--destinationagent-canvas, new-canvas, or a canvas handle. Required; may also be the first positional argument
--canvas-titleRequired when --destination is new-canvas
--promptThe instruction
--prompt-node node:NODE_IDUse an existing node’s text as the prompt
--referenceRepeatable, up to 16. A node:/asset: handle, a local raster path, -, or clipboard
--elementRepeatable, up to 8. An exact immutable element:<id>@v<n> handle
--count N1–4 outputs
--target node:NODE_IDRepeatable. Write into existing nodes instead of creating them
--target-node node:NODE_IDA single explicit target
--model, --connection, --size, --quality, --sourceGeneration settings
--title, --x, --y, --width, --heightGeometry for automatically created targets
--no-wait, --progress, --timeout, --intervalWaiting behaviour; default timeout 900 seconds
--webhook-url, --webhook-secret-envOne signed terminal callback
--base-revision, --idempotency-keyConcurrency and retry control

Local references must be PNG, JPEG, WebP, or GIF and at most 50 MB each. Identical files are uploaded once and reused. When --target is omitted, the CLI creates the target nodes for you.

Elements may be prompt-only, image-only, or both. Their source images share the same 16-reference limit as direct references. JSON input may add a role and influence, for example { "handle": "element:soft-window-light@v2", "role": "style", "influence": 0.8 }.

Image generation charges the AI provider connected to the account. Ask before running it, and do not retry a failure automatically.

gavana image generate \ --destination agent-canvas \ --model model:OPAQUE_MODEL_KEY \ --prompt "A studio product photograph on a seamless grey backdrop" \ --progress gavana image edit \ --destination canvas:OWNER_UID:CANVAS_ID \ --reference node:SOURCE_IMAGE \ --prompt "Warm the lighting and remove the reflection" \ --idempotency-key edit-hero-a gavana image variations \ --destination new-canvas --canvas-title "Variations" \ --reference ./source.png \ --prompt "Four alternate angles" --count 4

Output. The terminal Run with images, durable node: and asset: handles, and a destination object. --output markdown renders the results as image links.

Exit codes. 0; 2 for a missing destination or prompt, an invalid --count, or an unsupported reference; 3 for a missing scope; 4 if a reused idempotency key produced incomplete automatic targets; 8 if you stopped waiting; 9 on a terminal failure.


video

gavana video generate --model model:OPAQUE_MODEL_KEY --prompt "..." [--duration 15] gavana video download job:JOB_ID --file output.mp4 [--yes]

Video options

OptionEffect
--prompt VALUEVideo instruction. Required, maximum 8,000 characters
--duration SECONDSExact model-supported duration, a whole number from 1 through 120
--aspect-ratio VALUEExact model-supported aspect ratio
--resolution VALUEExact model-supported resolution
--first-frame VALUEnode:, asset:, a public HTTPS URL, a local image, -, or clipboard
--last-frame VALUEEnding frame; requires --first-frame
--reference VALUEModel reference image; repeat up to nine times
--canvas VALUECanvas containing any node: frame or reference
--audio / --no-audioRequest or disable generated audio when the model supports it
--download VALUEStream a successful generated video to this file

Also accepted: --model (required), --connection, --idempotency-key (8–200 characters), --no-wait, --progress, --timeout (default 1800 seconds), --interval, --yes.

Validation before spending. video generate resolves the model and checks your options against its declared capabilities and parameters before starting paid work. It rejects --first-frame on a model without video.generate.fromImage, --last-frame without video.generate.fromFrames, --reference without video.generate.fromReferences, and any duration, aspect ratio, resolution, or audio setting the model does not list. It also refuses a bare provider model id that matches more than one connection — use the model: handle.

Other rules: --last-frame requires --first-frame; --canvas is required when any frame or reference is a node: handle; reference values must be unique; HTTPS URLs must not contain credentials; and --download cannot be combined with --no-wait.

video download. Streams a completed Job’s output to a file. It refuses to overwrite an existing file unless you pass --yes, writing through a temporary file created with mode 0600 and cleaning it up on failure. Returns job, file, bytes, and contentType.

gavana model list --capability video.generate --jq '.models[].handle' -r gavana model get model:OPAQUE_MODEL_KEY --pretty gavana video generate \ --model model:OPAQUE_MODEL_KEY \ --prompt "A slow product turntable on a matte grey surface" \ --duration 15 --aspect-ratio 9:16 \ --download ./turntable.mp4 \ --progress gavana video generate \ --model model:OPAQUE_MODEL_KEY \ --prompt "Animate the fabric naturally" \ --first-frame node:PRODUCT_STILL \ --canvas canvas:OWNER_UID:CANVAS_ID \ --no-wait gavana video download job:JOB_ID --file ./result.mp4

Scopes. Generation requires canvas:read, asset:read, and video:generate. Waiting, downloading, and cancelling additionally require job:manage.

Exit codes. 0; 2 for an unsupported option for the chosen model, a missing prompt or model, or an existing output file without --yes; 3 for a missing scope; 8 if you stopped waiting; 9 on a terminal failure.


job

Image and Action work returns a run: handle; job: remains a compatibility alias for the same temporary record. Video generation returns a job: handle natively.

gavana job get job:JOB_ID gavana job wait job:JOB_ID [--progress] [--timeout SECONDS] [--interval SECONDS] gavana job cancel job:JOB_ID --yes

wait defaults to a 900-second timeout and a 1.5-second poll interval. --progress writes each state transition, including a percentage when the provider reports one, to stderr as JSON.

gavana job wait job:JOB_ID --progress --output markdown gavana job cancel job:JOB_ID --yes

Scopes. All three require job:manage.

Exit codes. 0; 2 for a cancel without --yes; 5 if the job does not exist or has expired; 8 on wait timeout; 9 if the job ended failed, canceled, or expired.


run

The shared observation interface for Recipe, image, and Action work.

gavana run get run:RUN_ID gavana run wait run:RUN_ID [--progress] [--timeout SECONDS] [--interval SECONDS] gavana run cancel run:RUN_ID --yes

Same defaults as job. A failed provider request is returned as a terminal Run result rather than a generic request error, so run wait finishes with a readable reason: a stable failure.code and a failure.retryable flag.

gavana run wait run:RUN_ID --output markdown gavana run get run:RUN_ID --jq '.status' -r gavana run cancel run:RUN_ID --yes

Scopes. All three require job:manage.

Exit codes. 0; 2 for a cancel without --yes; 5 if the Run does not exist or has expired; 8 on wait timeout; 9 on a terminal failed, canceled, or expired.


campaign (retired)

The campaign command surface is retired and disabled. Running it returns:

The retired campaign command surface is disabled. Use Recipe Library forks and explicit canvas/image commands instead.

It can be re-enabled for migration only by setting GAVANA_ENABLE_LEGACY_CAMPAIGN_COMMANDS=true (or the CRAFTBOARD_ENABLE_LEGACY_CAMPAIGN_COMMANDS alias). When enabled it exposes campaign plan, campaign start, campaign get, campaign review, and campaign cancel.

Do not build on it. Use Recipes and explicit canvas and image commands. See legacy campaigns for the API-side status.


Scopes by command

CommandScopes
auth status, version, capabilities, completion, config, mcpNone
doctorNone beyond whatever the credential already has
canvas list/get/render, node get, connection listcanvas:read
canvas create, canvas apply, node and connection writescanvas:read, canvas:write
asset list/getasset:read
asset uploadasset:read, plus image:generate or video:generate
element list/get/history, element collection-listelement:read
Element and Element-collection mutationselement:read, element:write
model list/get, provider list, ai-connection listimage:generate or video:generate
recipe search/getcanvas:read
recipe forkcanvas:read, canvas:write
action list/getcanvas:read
action runcanvas:read, canvas:write, asset:read
image generate/edit/variations, recipe runcanvas:read, canvas:write, asset:read, image:generate; add element:read when applying Elements
video generatecanvas:read, asset:read, video:generate
run get/wait/cancel, job get/wait/cancel, video downloadjob:manage
apiWhatever the target endpoint requires

A batch sent with validateOnly needs only canvas:read — validation does not write, and does not consume an idempotency key. The CLI’s canvas apply always mutates; use MCP canvas_validate or the API for validation.

Stable handles

Copy these from responses. Never construct one.

canvas:CANVAS_ID canvas:OWNER_UID:CANVAS_ID node:NODE_ID connection:CONNECTION_ID asset:ASSET_ID asset:OWNER_UID:ASSET_ID shared asset element:ELEMENT_ID element:ELEMENT_ID@v2 immutable revision element-collection:COLLECTION_ID recipe:RECIPE_ID action:SLUG model:OPAQUE_MODEL_KEY run:RUN_ID job:JOB_ID

Model, Run, and Job ids are opaque. Copy the complete returned handle; do not decode, guess, or assemble one.

Destinations

Every command that produces canvas output takes --destination:

ValueMeaning
agent-canvasThe account’s persistent Agent Canvas
new-canvasA fresh canvas. Requires --canvas-title and an 8–200 character idempotency key
canvas:OWNER_UID:CANVAS_IDAn existing canvas

With new-canvas the idempotency key determines the canvas identity — the CLI derives the new canvas id from a hash of the key, so re-running the same command with the same key reuses that canvas instead of creating another.

Pagination

Command--limit maximum
canvas list25
recipe search, action list, model list, provider list100
asset list200

Continue with --cursor set to page.nextCursor from the previous response, keeping the same filters. Cursors are opaque and at most 8,192 characters.

Webhooks

A signed callback replaces polling — useful for a start-only token that lacks job:manage.

read -rs 'GAVANA_WEBHOOK_SECRET?Webhook signing secret: '; printf '\n' export GAVANA_WEBHOOK_SECRET gavana image generate \ --destination agent-canvas \ --prompt "A studio product photograph" \ --webhook-url 'https://automation.example.com/hooks/gavana' \ --no-wait unset GAVANA_WEBHOOK_SECRET

The secret is read from GAVANA_WEBHOOK_SECRET by default, or CRAFTBOARD_WEBHOOK_SECRET, or whichever variable you name with --webhook-secret-env. It must be 32–512 characters, and the variable name must be a valid identifier. The CLI never prints the secret, and --webhook-secret-env without --webhook-url is a usage error.

Recipe, image, and Action starts all accept webhooks. Successful image and Action callbacks are sent only after Gavana has stored the durable canvas, node, and asset results. Failed, canceled, and expired callbacks describe the terminal state without requiring images. Recipe callbacks carry their typed terminal outputs. If a start-only token is revoked or expires mid-Run, the Recipe stops with delegation_revoked and sends its signed failed callback.

Webhooks are deliberately unavailable through MCP so a signing secret cannot enter model-visible tool arguments. See Webhooks.

Result retention

Image and Action run: handles and their legacy job: aliases address the same temporary record and expire together. The defaults: 24 hours while unacknowledged, 15 minutes after the first successful server finalization (from a Run GET or a callback), and seven days for the expired tombstone. Operators may override these windows. Recipe Run records currently do not use this temporary TTL.

Persist the returned asset: and node: handles — those stay durable after the observation handles expire.

Telemetry

For commands that reach the API, the CLI reports a start and finish event — command name, status, duration, and correlation ids — to /api/agent-analytics/events on your Gavana instance. Requests carry X-Gavana-Agent-Surface and correlation headers so a Run can be traced end to end. No canvas content, prompt text, or credential is included, and a failed report never fails the command.

Last updated on