@facelessad/cli
v2.2.0
Published
Create faceless video ads from your terminal or build scripts — the FacelessAd command line.
Readme
@facelessad/cli
Create faceless video ads from your terminal or build scripts.
npm install -g @facelessad/cli
facelessad login # paste an API key from facelessad.com/developers
facelessad tools # what can be made
facelessad create --tool motion-graphics --url https://your-product.com --waitCommands: login, create, status, list, download, estimate,
tools, voices, brands, balance, parts, regen-part, regen,
render, cancel, and webhooks / webhook-add / webhook-rm. Every
command accepts --json for scripting; create/estimate accept --dry-run
(print the request body without sending) and --file body.json (a base body
that flags override).
A finished video can be fixed part by part: parts <id> shows what it is made
of and regen-part redoes one clip, image card, graphics block — or, on
background-image styles (marked +bg in tools), one bg-N AI photo
backdrop with --prompt. The whole video re-renders automatically.
The key is stored in ~/.facelessad/config.json with mode 0600. The
environment variable FACELESSAD_API_KEY always wins — that is the way CI
should authenticate. FACELESSAD_API_URL overrides the API base.
--wait polls and prints phase + elapsed time, and stops watching after 6
hours (--timeout <seconds> changes that; --timeout 0 waits with no
limit). Stopping the wait never cancels the build — API videos run on a
bulk lane that can take hours when the queue is busy, and they keep going. A video whose status is
draft is not building and will never finish on its own, so --wait
stops on it immediately instead of polling forever. For long-running
builds a webhook is still the better tool — a stuck shell is a stuck CI
job. See https://facelessad.com/developers → Webhooks.
Getting the finished video
--wait tells you when the video is done, but it never prints the video URL.
Three ways to get the file:
facelessad download 103 --out ad.mp4 # saves into the CURRENT directory
facelessad status 103 --json | jq -r .url # the raw link, valid ~24 hoursThe third is the browser: every video made through the CLI also appears in My Files at facelessad.com.
Since 1.1.1 download prints the full path and the file size, and fails
loudly on an empty file instead of reporting success. The link is signed and
expires in about an hour — the video itself does not, so ask for the status
again to get a fresh one.
Custom style (1.1.0)
Most tools accept a look of your own instead of one of the built-in styles.
facelessad tools marks which ones (supports.customStyle):
facelessad create --tool motion-graphics --url https://your-product.com \
--style custom \
--custom-style "Retro terminal — phosphor green on near-black, scan lines, chunky mono type" --waitWhat to describe depends on what the tool builds. Animated Ad, Character, Music Video, Inspiration, Motivational and Slideshow render images, so describe the illustration or cinematic style. Motion Graphics, SaaS UI Ad, Text Animation and Video Banner generate their graphics as code, so describe typography, palette and motion. Crude Drawings has no custom style.
Product Showcase builds both — cinematic clips animated from your real product photo, and text graphics drawn in code on top — so it takes two descriptions:
facelessad create --tool product-showcase --product-image https://.../bottle.jpg \
--text "A steel bottle that keeps drinks cold for 24 hours" \
--style custom \
--custom-style "Frozen tundra — cracked ice, pale blue rim light, slow orbiting camera" \
--custom-graphics-style "Deep navy typography, thin white hairlines — the video reads light" --waitLeave --custom-graphics-style out and it is written for you from the first
description.
Your text is expanded into a full style specification before the video is
built. facelessad status <id> --json returns the expanded text as
customStyle; feed it back with --no-refine-style to give a whole
campaign one identical look.
Flag spellings
One spelling per flag. Up to 1.5.2 seven alternative spellings were also
accepted (--brand, --color, --colour, --voice-id, --voice-gender,
--no-voice-over, --product-image-url); they were removed in 1.5.3, because
two names for one flag is worse for a reader — and for an assistant — than one.
An unknown flag stops the command, so a removed spelling fails loudly rather
than building a video with the wrong settings.
These are not alternative spellings but forms of their own, and they stay:
| form | means |
|---|---|
| -o | short for --out |
| --no-captions | --captions false |
| --no-music | --music false |
| --no-sfx | --sfx false |
| --no-voice | no narration |
| --no-brand-kit | build without the Brand Kit |
| --no-refine-style | use the custom style text verbatim |
| --visual-direction-refine "<preset\|text>" | how the story is told, not how it looks (2.2.0) |
| --layout <A\|HA\|AC\|HAC\|AIA\|HAIA\|AIAC\|HAIAC> | card layout; data card drops if too short, hook/CTA stay (2.2.0) |
--no-brand-kit now works; before 1.0.1 it was accepted on the command line
and silently dropped, so the video was built with the Brand Kit anyway. An unknown flag is now an error rather than
something quietly ignored — that silence is what hid the whole problem.
2.2.0 — --visual-direction-refine
Animated Ad and Music Video: a preset id (metaphoric, realistic,
dramatic, calm, playful, dark, bright, magical — facelessad tools
lists them) or your own instruction up to 300 characters. Reshapes how the
visual story is told, not how it looks; applied on top of --visual-direction
or the generated one. Free.
--layout (same two tools) picks the card layout. Too short for the data
card? It is dropped, the hook/CTA you chose stay, and the output says so.
facelessad tools lists the layouts and what each becomes per duration.
1.3.0 — turn any text file into a video
Agent skill included. The package ships SKILL.md — drop it where your
agent reads skills and Claude Code, Cursor, Codex or OpenClaw can operate
the CLI on its own (every command supports --json):
npm install -g @facelessad/cli
cp "$(npm root -g)/@facelessad/cli/SKILL.md" .claude/skills/facelessad/SKILL.md # Claude Code
# Cursor / Codex / OpenClaw: point your rules or skills folder at the same fileThen just ask: "Take our latest release notes and create three launch videos using our brand kit."
--materials-file <path> reads a local text file — a README, release
notes, a blog post, product JSON, docs — and uses it as the materials:
facelessad create --tool motion-graphics --materials-file README.md --wait
facelessad create --tool animated-ad --materials-file release-notes.md --duration shortSend the whole file. Text over ~8000 characters is condensed server-side in a
way that keeps that material's own details, numbers and voice — it is not
squeezed into a generic brief, so a README and a blog post still produce
different videos. The hard ceiling is 200 000 characters, and the CLI checks
it before sending. Combines with --text (both are used).
--materials-url <url> is a direct link to a raw text file on the
web — a GitHub raw README, a gist, a docs export. The server fetches it with
the same protections as landing pages, but without HTML extraction:
facelessad create --tool saas-ui-ad \
--materials-url https://raw.githubusercontent.com/you/repo/main/README.md --waitUse --url for HTML pages (the page text is extracted) and --materials-url
for files that already are text. There is also POST /api/v1/files for
uploading a file once and reusing it across many videos via
materials.file_id — the CLI doesn't need it (it inlines the file), but
other integrations might; see the developers page.
1.2.0
Video Banner has flags. Its copy and images were reachable through the
HTTP API but had no flags at all, so the only way to set them was
--file body.json:
facelessad create --tool video-banner --url https://example.com \
--headline "Ship ads in minutes" --subline "No camera, no crew" \
--cta-text "Try it free" --badge random_face --photo-query "nordic office" --waitLeave the three texts out and they are written for you from the materials.
Give a headline and only your own copy is used — the fields you leave empty
stay empty rather than being written for you (--text-mode makes the choice
explicit). --background-image <url> replaces the automatic photo pick
entirely, and --badge-image <url> puts your own image in the corner instead
of the AI face.
Product Showcase extra angles: --product-images url1,url2,url3 (up to 8),
which improves how well the product keeps its shape and label across clips.
SaaS UI Ad takes screenshots. The tool decomposes real screens with vision and rebuilds them animated — that is the whole point of it, and without them the ad is built from your written description alone:
facelessad create --tool saas-ui-ad --url https://your-app.com \
--screenshots "https://.../dashboard.png,https://.../editor.png" --waitHow many fit depends on the length, because each screen needs time on camera:
1 for short, 3 for medium, 4 for long. facelessad tools --tool saas-ui-ad
prints the limits.
Character does dialogue. --speakers 2 makes it a conversation between two
characters instead of one narrator, and the ad structure has to match — the
two pools are listed by facelessad tools --tool character:
facelessad create --tool character --url https://your-product.com \
--speakers 2 --structure ch-qa --gender-right male --wait--voice-right <id> and --gender-right female|male steer the second voice.
Leave them out and a distinct one is picked for you — a different gender
first, then a different id. They need --speakers 2; without it the command
stops before it reaches the network.
A flag that needs a value is an error without one. --brand-kit on its own
used to mean brand number 1, and download 5 -o used to write a file called
true. Both now stop with missing_value. Flags that are booleans by nature
(--sfx, --no-voice, --wait) are unaffected.
Boolean flags are validated. --captions yes, --captions 1 and
--captions TRUE used to become false without a word, because only the
exact string true was accepted. All the usual spellings now work in both
directions, and anything else is an error instead of a silent guess.
facelessad tools --tool <id> shows every ad structure, grouped the way
the app groups them. It used to print only the default group — 17 of animated
ad's 46, and 6 of motion graphics' 119.
--winners / --no-winners are gone. Winning-ad analyses are not offered
through the API while the feature is unfinished; the server no longer reads the
field.
Errors are printed with the API's own wording and exit code 1. The machine
code (e.g. unknown_style) follows the message.
