The visual builder
A React Flow canvas with a node palette on the left, a node inspector on the right, and a live preview at the bottom. Drag, drop, connect, save.
Layout
- Left: the node palette. Triggers and actions grouped by category.
- Center: the canvas. Drag nodes here, connect them by dragging edges between handles.
- Right: the inspector. Click a node to edit its fields; click an edge to set its branch label.
- Bottom: the live preview log. Hit Run preview to see what your workflow would do, end-to-end, without touching a real device.
Adding nodes
Drag a node from the palette onto the canvas. Every node is given a generated id; you can't change it but you can give it a human-friendly label in the inspector. Labels appear on the canvas and in run traces.
Connecting nodes
Drag from a node's output handle (right side) to another node's input handle (left side). Edges between regular nodes have no label. Edges leaving a action.branch node carry a true or false label — set it from the inspector when the edge is selected.
Validation
The builder runs the workflow through the same Zod schema the API uses (see DSL reference). Any node with an invalid field gets a red ring; clicking it shows the specific validation message in the inspector. The Save button is disabled until the workflow validates.
workflows.create.Live preview
Click Run preview to execute the workflow through our in-browser TypeScript interpreter. The simulator adapter logs every action without actually doing anything — no taps, no HTTP calls, no SMS. The trace it produces matches what your phone will produce when the workflow runs for real.
Why does this matter? Because parity is enforced in CI: every fixture in packages/workflow-engine/fixtures/ runs through both the TS and the Kotlin interpreter and we assert byte-equal traces. If the builder preview produces one outcome and the phone produces another, that's a bug, not a feature.
Versions
Every save creates a new workflow_version. The Versions dropdown on the builder lets you load any earlier version into the canvas; saving from there creates a new version again (forks aren't branching trees — we keep linear history).
Pushing to a device
Save is automatic-sync. Once you click Save, the API:
- Validates the workflow.
- Persists the new version.
- Updates the workflow's
currentVersionIdpointer. - Pushes an FCM message to every device this workflow is assigned to.
On the phone, the FcmService receives the push, pulls the new definition, and rearms the triggers. End-to-end latency from Save to a phone being ready to run the new version is typically under 5 seconds.