photocli
photocli is a complete, self-documenting command-line interface for the
PhotoRoom API.
It is designed to feel good in a terminal and to be unambiguous to AI agents.
It implements every current PhotoRoom operation:
- Image Editing v2 (
GET and POST /v2/edit), including every documented
edit, generation, preview, and advanced parameter.
- Remove Background (
POST /v1/segment).
- Account Details (
GET /v2/account).
The edit surface is generated from an embedded snapshot of PhotoRoom's official
OpenAPI reference. Exact dotted API names are retained as flags, and --param
and --file-param provide a forward-compatible escape hatch.
Install
On macOS, the primary installation is Homebrew:
brew install --cask julienmarie/tap/photocli
photocli setup
Signed and notarized universal macOS archives, plus Linux amd64 and arm64
archives, are published on GitHub Releases. If you run a downloaded binary
directly, photocli setup offers to install it to /usr/local/bin/photocli.
Build from source with Go 1.25.12 or later (earlier 1.25 patch releases contain
standard-library networking and TLS vulnerabilities):
go install github.com/julienmarie/photoroom-cli/cmd/photocli@latest
photocli setup --skip-install
Interactive setup hides terminal input:
photocli setup
For automation, avoid command-line arguments and shell history:
printf '%s\n' "$PHOTOROOM_API_KEY" | photocli setup --skip-install --api-key-stdin
The selected v1 storage model is ~/.config/photocli/config.toml (or
$XDG_CONFIG_HOME/photocli/config.toml), with a 0700 directory and 0600
file. PHOTOROOM_API_KEY overrides the file. This protects against other local
users but, unlike a platform keychain, the key remains plaintext for its owner.
Run photocli doctor to inspect installation, permissions, credential source,
and API connectivity without revealing the key.
Use
# Basic API
photocli remove-background product.jpg --format webp --output product.webp
# Plus API: edits compose freely
photocli edit product.jpg \
--background.color FFFFFF \
--shadow.mode ai.soft \
--output listing.png
# URL input selects GET automatically
photocli edit https://example.com/item.jpg --background.blur.mode bokeh
# Local/file input selects multipart POST automatically
photocli edit product.jpg --background.imageFile studio.jpg
# Create an image without an input
photocli edit \
--imageFromPrompt.prompt 'a red car in the desert' \
--imageFromPrompt.size SQUARE_HD
# Virtual Model, with nested fields discovered directly from OpenAPI
photocli edit shirt.jpg \
--virtualModel.mode ai.auto \
--virtualModel.model.preset.name avery \
--virtualModel.scene.preset.name street \
--virtualModel.pose standing \
--removeBackground false \
--referenceBox originalImage
# Streams are supported; diagnostics stay on stderr
cat product.jpg | photocli edit - --output - > result.png
# Inspect quota
photocli account
Image commands refuse to overwrite existing files unless --force is used.
Billable requests are not retried automatically. account requests retry
transient failures safely.
Discoverability for agents
Everything needed to formulate a call is available offline and without a key:
photocli --help
photocli describe --recursive --format json
photocli describe edit --format json
photocli api operations
photocli api schema --format json
photocli examples agents
photocli completion zsh
Before consuming credits, resolve and validate a request locally:
photocli edit product.jpg \
--background.prompt 'on a marble counter' \
--dry-run
--dry-run includes the chosen HTTP method, exact wire fields, file mappings,
headers, and output path. Authentication is always redacted. --json makes
status and error envelopes compact, and commands use stable exit categories:
Fields containing {key} are dynamic map templates. Replace {key} with the
desired layer/reference name and pass the result through --param or
--file-param, for example --param layers.hero.text.content=SALE. Fields
containing [] are repeatable exact API names; quote those flags in shells that
interpret brackets.
2: invalid invocation or unsafe output.
3: local configuration or I/O failure.
4: authentication/authorization failure.
5: insufficient credits/payment required.
6: API, network, timeout, or server failure.
Maintain the API snapshot
Refresh from the official documentation and review the resulting diff:
go generate ./internal/spec
go test ./...
CI independently refreshes the snapshot and fails when the checked-in contract
has drifted. Releases never fetch a changing API definition during a build.
Release prerequisites
Tagging v* runs GoReleaser, creates a universal macOS executable, signs and
notarizes it through Apple's service, publishes Linux builds, checksums, SBOMs,
and keyless Cosign v3 bundles, and updates julienmarie/homebrew-tap.
Configure these GitHub Actions secrets:
MACOS_SIGN_P12 and MACOS_SIGN_PASSWORD — base64 Developer ID Application
certificate and password.
MACOS_NOTARY_KEY, MACOS_NOTARY_KEY_ID, and
MACOS_NOTARY_ISSUER_ID — App Store Connect notarization key material.
HOMEBREW_TAP_TOKEN — token with contents write access to the tap repository.
For a local release, export the same variables before running GoReleaser. The
local Developer ID certificate is present on the development Mac; the
AC_NOTARY keychain profile must still be created if using notarytool
directly. scripts/verify-macos-release.sh performs universal architecture,
signature, Gatekeeper, and CLI smoke checks on a released archive.