Skip to content
ChatfuelSDK
Reference

module.json

Every field of a module manifest — the required five, the two enums, the regex patterns, and what reads each one.

Five fields are required — id, name, description, status, skill — and additionalProperties is false, so a field the schema does not know is a failed gate rather than a silent no-op.

The schema is packages/module-manifest/module.schema.json and it is the authority. The ModuleManifest interface beside it is the same document in TypeScript; the two are changed together, and pnpm validate fails on drift.

The top level

Prop

Type

There is no routes field and no nav field. Where a module appears in the app is the shell's — see the module contract.

What status and selection actually do

ValueEffect
status: "planned"The module is in the tree and validated like every other, and ready() filters it out. The picker never offers it, and --modules naming it ends the run with the list of ids that are ready.
status: "ready"Offered.
selection: "default"Taken by --yes.
selection: "opt-in"Offered in the picker with an extra hint, and never taken by --yes — name it in --modules to get it. Two modules use it: auth and admin, both because they need credentials the wizard cannot invent.

An app preset's module list goes through the same gate as --modules: an id that is unknown or not ready ends the run, naming which of the two asked for it.

requires is resolved into a closure before anything is written: dependencies first, core always, and the ids it added are printed. One module declares one — deals requires contacts.

recommends is offered in an interactive run as a second checklist, and merely logged when the selection came from --modules, --yes or an app preset. A mutual recommendation between two modules is legal where a requires cycle is not. The longest list is publishing, which recommends auth, admin and automations.

hidden

A hidden module contributes no rail item and no /<id> route. The flag lives in the manifest as well as on the shell descriptor because the wizard writes the agent handoff from manifests alone and would otherwise print a deep link to a route that does not exist — and the two are compared, so they cannot drift. A hidden module still gets a line in the handoff's tour, without a route.

auth is the only module that sets it: it contributes a host integration instead of a page. railHidden, which keeps the route and only takes the listing away, is a descriptor field with no manifest counterpart — see the module contract.

skill

Prop

Type

app.env

An array of the variables this module needs. The wizard writes the union across the selected modules, first declaration wins, preceded by the app's own four — VITE_APP_NAME, VITE_APP_LOGO, VITE_CHATFUEL_DASHBOARD_URL, ALLOWED_ORIGINS — which belong to no manifest. An app preset's declarations come last, so a preset can add variables but never redefine one a module declared.

Prop

Type

Values resolve in this order: a step-resolved value, then the token for CHATFUEL_TOKEN, then the manifest default, then ''.

resolve documents which step fills the variable; no code reads the field. The steps write into the answer bag by variable name — the workspace picker sets VITE_CHATFUEL_WORKSPACE_ID and CHATFUEL_WORKSPACE_ID, the auth setup sets the SUPABASE_* ones, the admin step sets ADMIN_PASSWORD — so a resolve value that is wrong changes nothing, and a variable nobody writes comes out empty however it is annotated.

A .env line has no escape, so the writer refuses a resolved value carrying a line break, by variable name, before the file exists.

app.embed

What --embed copies into a host project.

Prop

Type

The wizard installs the union of npmDependencies across the selected modules, and drops an unselected module's from the scaffold's package.json — a name a selected module also declares stays.

permissions

Prop

Type

Nothing checks these at runtime. They are read once, when the wizard writes the handoff notes, so the agent that continues the work knows what the token has to be allowed to do. A module may declare none — auth does not, because it gates the app rather than calling Chatfuel — and the handoff then prints (none declared).

What the gates check

pnpm validate runs both of these; a failure is a failed build, not a warning.

CheckFailure
A directory under content/modules/Every one needs a module.json — the skill lint, the migration hygiene and the codegen coverage all reach a module through its manifest, and would skip it in silence.
ajv against the schemaReported per field path — a bad pattern, a value outside an enum, a property the schema does not have.
id vs the directoryid "x" does not match directory name "y".
skill.installAsAlready used by another module.
The skill directoryDeclared but not in the tree.
SKILL.md frontmatterIts name must equal installAs.
requires / recommendsMay not name themselves, or a module that does not exist.
requires cyclesDetected over requires edges only.
The shell side, for a ready module with an app blockcontent/shell/src/modules/<id>/index.tsx exists and exports moduleDescriptor under that exact name; its hidden matches the manifest's; the id is in the shell registry; content/modules/<id>/handoff.md exists; every embed root, the playbook and the entry component resolve.
The shell registryIt may list nothing that is not a ready module with an app block.

On this page