Publishing a Workflow as an App
Applies to: ChengOS v0.1.0+ | Last verified: 2026-08-12 | Source:
chengflow-ui/src/features/apps,crates/cheng-nodes/src/nodes/builtin/ui,crates/cheng-api/src/rest/handlers/ui_routes
A workflow that only runs in the editor is a tool for you. Publishing turns it into something other people use — a page with a URL, a chat interface, or an endpoint another system calls.
Three ways to publish
Pick by who the audience is:
| Route | What the user gets | Best for |
|---|---|---|
| Page nodes | A real web page with layout, navigation, and access control | Forms, dashboards, internal tools |
| Chat gateway | A chat interface — widget, H5/PWA, or a messaging channel | Conversational assistants |
| API / MCP | An endpoint another system calls | Programmatic integration |
Make the workflow publishable
Two things first, in this order.
Set the state to active. A new workflow is draft. Draft is for building; active
declares it ready to be executed and referenced by other things. Publishing a draft is the most
common reason a published app appears to do nothing.
Decide the entry point. Which node receives input from the outside determines what shape of
publication makes sense — io/input_text for a form, chat/input for a conversation,
GatewayReceiverNode for external app messages.
Building a page
Nine ui/* nodes compose a page. They are a static page skeleton generator, not a runtime
page engine: they collect configuration and output a serializable PageLayout JSON. All runtime
work — loading page config, applying operational overrides, running data loaders — is the
frontend's job.
That split is why pages are fast: validation happens entirely at build time, at zero runtime cost.
| Node | Role |
|---|---|
ui/page_root | The page container: layout template, theme, and the assembly point |
ui/header · ui/footer | Top and bottom regions |
ui/top_nav | Navigation bar |
ui/left_sidebar · ui/right_sidebar | Side regions |
ui/content | The main content area |
ui/route | Maps a URL path to a page |
ui/rbac_guard | Access control on a route |
Connect your region nodes into ui/page_root, then a ui/route to give it a path. Published
routes resolve through GET /api/v1/routes/by-path.
ui/page_root validates in three layers, all at build time: field formats (hex colours, URLs,
enum values), region dependencies (a layout template that conflicts with the regions you
actually supplied), and deep semantic checks (duplicate menu keys, colliding modal ids). A page
that would render broken fails to build instead.
Access control
ui/rbac_guard outputs a GuardResult that connects to a ui/route node's guards port.
Rules can check a role, check a permission, match a user attribute, or evaluate a dynamic
expression, and a guard declares its own fail action — so "not permitted" can redirect rather
than dead-end.
Guards are evaluated server-side. A guard is an access control decision, not a UI hint: hiding a control in the browser is not protection, and the guard is what actually stops the request.
Publishing as a chat interface
Rather than building pages, bind the workflow to a channel and let the gateway render the chat
UI. A channel carries a channelId (which app the message came from) and a boundWorkflowId
(which workflow runs) — bound at channel-configuration time, never inferred. See
the gateway model and SDK quickstart.
This is the route for WhatsApp, Telegram, Slack, DingTalk, and WeCom, all of which have first-party adapters, as well as for an embedded web widget.
Publishing as an API
Any active workflow is already executable over REST:
curl -X POST http://127.0.0.1:19225/api/v1/executions \
-H 'Authorization: Bearer <token>' \
-H 'X-Workspace-Id: <workspace-uuid>' \
-H 'Content-Type: application/json' \
-d '{"workflow_id": "<uuid>", "inputs": {"user_input": "hello"}}'
Subscribe to /ws/executions for live results, or poll GET /api/v1/executions/:id. With
MCP_ENABLED=true workflows are also exposed as MCP tools, so an external agent can call your
workflow the way your agent calls anything else.
Templates
Marking a workflow as a template makes it a starting point for others
(PUT /api/v1/workflows/:id/template). GET /api/v1/workflows/templates is public and
unauthenticated, which is what lets a template gallery load before sign-in.
Next steps
- Gateway model — apps, channels, and sessions.
- SDK quickstart — embedding a chat UI.
- REST API overview — the endpoints behind all of this.