Skip to content
ChatfuelSDK
Reference

The CLI

Every command and flag of npx @chatfuel/wizard, what each one falls back to when you omit it, which answers come from the environment, what --yes, --dry-run and --plan refuse to do, and the exit codes.

The scaffold's flags are declared on the program rather than on a subcommand, so --dir, --dry-run, --plan and --verbose work on update and auth as well as on the scaffold, and --yes on auth. update adds three flags of its own. The parser, the run and the subcommands load on demand, so --help, --version and a mistyped flag answer without loading the scaffold.

Commands

CommandWhat it does
npx @chatfuel/wizardScaffolds the app, or with --embed copies the modules into the project you are standing in. No command is the scaffold; the steps are on What the wizard does.
npx @chatfuel/wizard doctorPrints what the wizard can see before it asks anything, one row each: Node against the floor of 22.19.0; the outbound proxy it resolved; which coding agents are on PATH and where each reads skills; whether the content is a published package or a repository checkout, and its root; how many files the content lock pins; whether the content cache is writable; how many modules are ready of the manifests it has; which commit following the branch lands on and how it got there — floor, resolved, cached or pinned, with the reason when it fell back; and one real file fetched from that commit and checked against its digest. A missing agent is a warning; a fallback to the floor is reported and passes; anything else is a hard stop, and the command exits 1.
npx @chatfuel/wizard update [dir]Brings an app the wizard wrote up to the content this wizard resolves to. It reads .chatfuel/lock.json and puts every file in one of three states, decided by digest: upstream unchanged (skipped), upstream changed and still the bytes the wizard wrote (overwritten), upstream changed and edited since (left exactly as it is, reported as a conflict). --dry-run lists without writing, --json prints the plan for the chatfuel-update skill with the upstream copy of each conflict fetched beside it, and --resolved <paths…> records conflicts as settled. The directory is the argument, else --dir, else the one you are standing in. The procedure is Update an app.
npx @chatfuel/wizard authRotates the Chatfuel token in an app that already exists. It identifies the app by the line it is about to rewrite — a .env that declares CHATFUEL_TOKEN, or the empty # CHATFUEL_TOKEN= placeholder a declined gitignore leaves behind — refuses a .env that is a symlink, checks the new token against the API before writing, runs the same .gitignore guard the scaffold runs, and rewrites the file at mode 0600 with the token on its first line. Runs in the current directory unless --dir says otherwise.

Flags

FlagWhat it doesWhen you omit it
-v, --versionPrints the wizard's version.—
-y, --yesAccepts every default and skips every confirmation.false — every question is asked, and a run with questions and nothing attached to the terminal stops before it spends anything.
--dry-runStops before creating anything in your Chatfuel or Supabase account. The app is still written.false
--planPrints what the run would do and writes none of it: no directory, no file, no skill. Implies --dry-run.false
--verboseAdds the transport's own message under the wizard's, for a failure that came from a network call.false — you get the wizard's sentence and its hint.
--embedCopies the selected modules into the project you are standing in instead of scaffolding a new app.false. --yes alone stays standalone; only --embed chooses embed, and --app refuses it.
--dir <path>Where to write the app, resolved against the directory you started the wizard in. A directory that exists and is not empty is refused.Prompted, defaulting to ./chatfuel-app; ./chatfuel-app under --yes. In embed mode, for auth and for update, the current directory.
--modules <ids>Comma-separated module ids to install without prompting. An unknown id, or one that is not ready in this build, ends the run and lists the ready ones. Modules your selection recommends are named, not offered.Prompted. Under --yes: every ready module except the opt-in ones, which are auth and admin — name those explicitly to get them.
--app <slug>Scaffolds a preset app from the apps catalog: the preset decides the modules and the brand, its overlay lands over the scaffold, and its playbook goes to the coding agent as the build plan. Refuses --embed and --modules; --app-name and --logo still win over the preset's. A catalog other than the official one is described and confirmed before it is fetched.No preset: the module picker.
--apps-repo <url>Where the catalog is cloned from: an https:// or ssh:// URL, a git@host:owner/repo remote, or a path on this machine. Only with --app, and the one way to name a non-default catalog under --yes.CHATFUEL_APPS_REPO from the environment, else https://github.com/chatfuel-lab/chatfuel-apps.git.
--apps-ref <ref>The branch or tag of the catalog to clone. The catalog carries no digests and is pinned by nothing, so this is what makes a preset run repeatable. Only with --app.The catalog's default branch.
--agent <id>claude or codex: whose skills layout the run is written for. Anything else is refused before the first prompt.Whichever CLI is on PATH; the picker appears only when both are, and --yes takes the first one detected. Claude Code reads .claude/skills, Codex reads .agents/skills, and neither reads the other's.
--app-name <name>The app's own name: browser tab, top bar, sign-in screen. 1–60 characters, one line.Prompted, defaulting to the Chatfuel workspace's title, then to Chatfuel App; with --app, the preset's name.
--logo <path>An image file that becomes the app's logo and tab icon: .svg, .png, .jpg, .jpeg, .webp, .gif or .ico, under 1024 KB. Judged before the first prompt.Prompted; an empty answer keeps the mark the template ships, which is a real logo and not a placeholder. --yes never asks.
--workspace <id>The Chatfuel workspace this deployment starts in. An id that is not on the account ends the run and lists the ones that are.Prompted. Taken silently when the account owns exactly one. Under --yes without the auth module: the first workspace that holds a bot. Under --yes with auth: refused, because which workspace pays is not a question to answer on somebody's behalf.
--pricing <id>The Chatfuel plan the trial starts on, by pricing id. Monthly plans only. An id the catalogue does not sell ends the run and prints every plan with its id, its price and its AI credits.Prompted when the workspace has no subscription: Business, then the Agency tiers, each with its monthly price, its AI credits and its bot limit. Business under --yes. Not asked at all when the workspace is already subscribed.
--supabase-token <pat>Supabase personal access token for the Management API path. Resolved once, kept in memory, masked in every line the wizard prints, written nowhere.SUPABASE_ACCESS_TOKEN from the environment, else prompted — and a rejected token is re-asked, up to three times.
--supabase-project <ref>An existing Supabase project ref; skips the project picker. A paused project is refused with the link to restore it.The picker. Cannot be combined with --supabase-create.
--supabase-create <name>Creates a Supabase project with this name, 1–64 characters, or reuses the one already called that — so re-running a script leaves one project, not two. Two projects with the name is a refusal naming both refs.The picker. Cannot be combined with --supabase-project.
--supabase-org <slug>Which organization --supabase-create puts the project in.The only organization the token sees; a prompt when it sees several; under --yes with several, a refusal that names the slugs.
--supabase-region <code>Where --supabase-create places the project.Prompted, preselecting the recommended region; that recommended one under --yes.
--supabase-url <url>Manual path: the project URL, any https:// host with no path. A host that is not supabase.co gets a warning, not a refusal.The access-token path.
--supabase-anon-key <key>Manual path: the anon / publishable key. Set with --supabase-url; the pair selects the manual path, which makes no Supabase API call and ships the SQL for you to run.The access-token path.
--app-url <url>The origin the app is served from, added to the Supabase redirect allowlist so sign-in works on that domain. https:// only; http:// is accepted for localhost and nothing else.Prompted, and skippable. The deploy step adds the deployed origin itself when it knows it.
--admin-password <value>The admin module's password. Sixteen characters at least, no spaces, checked before the first prompt.ADMIN_PASSWORD from the environment, held to the same rule, else a generated one: 24 random bytes as base64url, written to .env and printed once at the end.
--signup <mode>Who may open an account on a Supabase project this run creates: open, with no email confirmation; confirm-email; or closed, which turns the sign-up form off. A project you brought keeps its own settings.Prompted on a project the run creates; open under --yes, with a warning that anyone who reaches the app can spend your Chatfuel account.
--allowed-origins <list>Browser origins besides the app's own that may call the proxy, comma- or space-separated, judged before the first prompt. * allows every origin and may not be mixed with names.ALLOWED_ORIGINS from the environment, else prompted. Under --yes the proxy answers its own origin only.

The variables read instead of prompted

A command line is not private: ps shows it to every process on the machine, and the shell writes it to a history file that outlives the run. So the answers that are credentials come from the environment, and passing --supabase-token or --admin-password instead earns one warning line, before the value is used, naming the variable that does the same job.

NameRead byWhat happens without it
CHATFUEL_TOKENThe token step, on its first attempt. There is no flag for it.Under --yes, or with no TTY, the run stops and says whether the variable is missing or was rejected, rather than falling through to a prompt nobody can answer.
SUPABASE_ACCESS_TOKENOnce, when the run starts; the auth step and the deploy step read it from memory.Under --yes the auth module refuses unless --supabase-url and --supabase-anon-key are both given. A token on its own is not enough either: it does not say which project to use, so pair it with --supabase-project or --supabase-create.
SUPABASE_SERVICE_ROLE_KEYThe auth step, manual path only.Prompted, and skippable. When it is set, the wizard names it next to the project it would be written for, because nothing can check that the key belongs to that project: at a prompt you can decline it, under --yes it is used with a warning.
ADMIN_PASSWORDThe admin step, when --admin-password is not given.A generated password. When it is set, it has to clear the floor the flag clears — sixteen characters, no whitespace — or the run stops saying so.
ALLOWED_ORIGINSThe origins step, when --allowed-origins is not given.Prompted; under --yes, the app's own origin only.

--app reads two more: CHATFUEL_APPS_REPO, which names the catalog the way --apps-repo does, and GITHUB_TOKEN or GH_TOKEN, tried once when a clone over https:// fails without credentials. Three control where the wizard's own content comes from — CHATFUEL_CONTENT_REF, CHATFUEL_CONTENT_ORIGIN and CHATFUEL_WIZARD_CACHE. Every variable the wizard reads, with its value, is on the environment page; why the content is fetched at all is Where the content comes from.

What --yes will not do

Two steps are skipped entirely under --yes, and for the same reason: a deployment is a public URL with your Chatfuel token behind it, and a repository under your GitHub account holds the source of an app wired to your bot. Neither is something a non-interactive flag should be able to cause.

StepAlso skipped when
Deploy to Vercel--dry-run, no TTY, embed mode, or the app has no vercel.json.
Push to GitHub--dry-run, no TTY, or embed mode.

Embed mode's dependency install is refused on the same grounds: an unattended run must not write to a project it did not create. Under --yes the wizard also starts no agent, offers to install none, starts no dev server, takes the Business plan unless --pricing names another, prints its checkout link and moves on, leaves sign-up open on a Supabase project it creates and admin recovery links off, and adds the .env line to .gitignore without asking — and says that it did.

It also refuses one thing outright: an app preset from a catalog named only by CHATFUEL_APPS_REPO. That variable can predate the command by months, a preset is trusted code, and under --yes there is no prompt left in which anyone would notice — so the flag proceeds with a warning and the variable is a refusal naming itself.

The one thing it will not replace is a skill directory the app's lock does not record as the wizard's. A person is asked; --yes leaves the directory exactly as it is, says so, and records it as kept, so update skips it too.

What --dry-run and --plan stop

--dry-run stops account assets, not files. The scaffold still runs: the app is written to disk, its lock beside it, the skills installed and the dependencies with them. What it holds back is everything that would exist on an account afterwards: the trial checkout session, the Supabase project creation, the database migrations, the auth-config PATCH and the recovery email template — each printed as the call it would have made, with .env then carrying empty Supabase values. An account that owns no workspace gets a stand-in instead of a real one, and the steps that would spend it are guarded the same way. The deploy and GitHub steps, the agent install and the dev server are skipped outright.

--plan stops files as well, and implies --dry-run. Every question is still asked and every read-only call still made — the token check, the workspace list, the Supabase lookups — and then the run prints what it would have written, in order and with paths: the app directory, the vendored trees, the migrations, the .gitignore line and .env, the skills, .chatfuel/lock.json, the install, the agent's checklist and instructions file. In embed mode the same, against your own project. Nothing is created. The one write that still happens is the wizard's own content cache, filled before any of this, and that directory is not your project.

Exit codes

CodeWhen
0The run finished. doctor with nothing failed — a missing coding agent is a warning, not a failure. update, conflicts included: a conflict is a file held back for you, not an error.
1The wizard stopped on something it can name, and printed its sentence, its hint and the Discord link. A cancelled prompt is one of these; so is doctor with a failed check. Anything the wizard cannot name is rethrown as a stack trace.
130Interrupted with Ctrl+C while the wizard was working. The cleanups the current step registered run first, newest first: a directory this run created is removed, a directory that was already there is left with what was written into it, a clone of the apps catalog is deleted, and an interrupted update records in the lock what it had copied.
143The same, on SIGTERM.

On this page