Skip to content
ChatfuelSDK
Concepts

Architecture

Three things with three lifetimes — a CLI that writes an app, an app you own, and the Chatfuel API it talks to.

npx @chatfuel/wizard is a CLI that writes an app. When it finishes it is gone — no framework of ours between you and your code, nothing in package.json pointing back at our repository, and nothing we publish later that can change what is already on your disk.

So "Chatfuel SDK" is three things with three lifetimes, and most confusion about it comes from treating them as one.

@chatfuel/wizard  ──writes──▶  your app  ──talks to──▶  Chatfuel
   (npx, once)                (yours, forever)          (GraphQL + WebSocket)

The CLI runs once

The wizard's whole job is a directory copy with edits. It copies apps/shell whole, deletes the module subtrees you did not pick, regenerates the module registry, filters the navigation table, copies the three source packages in, and rewrites the marked import lines that point at them.

Module manifests are read at that moment and never again: there is no manifest, no registry and no plugin loader inside the app at runtime. What the manifests decided is already baked into the files you have.

The app is yours

What lands is a React + Vite project. Three trees inside it came from us as source rather than as dependencies — the design system under ~ui, the typed API client under ~api, and the proxy under vendor/chatfuel-proxy — and none of them is reachable through node_modules. That trade is the subject of its own page: vendoring, not depending.

The API it talks to

~api is a generated, typed GraphQL client: queries and mutations over HTTP, live updates over graphql-ws, and REST only for uploads. The schema snapshot and the generated documents are refreshed by scripts rather than hand-edited.

In the browser the client is created with no token at all — it points at your own origin and the proxy attaches the token server-side. See the token boundary.

Errors arrive in one envelope shape, and classification reads the GraphQL extensions.code rather than the HTTP status. A module therefore never parses a transport error itself; it gets a typed refusal with a code it can branch on.

What lives where in the repository

If you are reading the source rather than the app it writes, the three sides are three directories.

PathWhat it is
packages/wizardthe CLI
apps/shellthe app it writes — the template is the real app, not a copy of one
packages/ui, packages/api-client, packages/vite-plugin-proxythe source trees it copies in
modules/<id>per module: the manifest, the handoff note and the agent skill
apps/shell/src/modules/<id>per module: the React tree

A module is two halves in two places, which is the one thing about this layout that surprises people — see modules.

What this costs you

Owning the code means owning the bugs in it. Nothing we publish can break your app, and equally nothing we publish can fix it: an upgrade is a re-run of the wizard or a diff you apply yourself, never npm update.

On this page