.agents/features/flows.md
Flows are the core automation primitive in Activepieces. Each flow is a versioned directed graph of trigger and action steps stored as a JSONB tree. The module handles the full lifecycle: draft editing via a single-endpoint operation dispatch, publishing (locking a version and registering the trigger source), enabling/disabling, folder organization, sample data capture for testing, human-input forms/chat interfaces, and the visual builder frontend powered by XYFlow. All 26 flow modification types are dispatched through one endpoint (POST /v1/flows/:id) with a discriminated-union body.
packages/server/api/src/app/flows/flow/flow.service.ts — core service (operations, publish, enable/disable)packages/server/api/src/app/flows/flow/flow.controller.ts — REST controllerpackages/server/api/src/app/flows/folder/ — folder CRUDpackages/server/api/src/app/flows/step-run/ — sample data capture and test-step executionpackages/server/api/src/app/flows/flow/human-input/ — form and chat public endpointspackages/shared/src/lib/automation/flows/flow.ts — Flow, PopulatedFlow typespackages/shared/src/lib/automation/flows/flow-version.ts — FlowVersion, FlowVersionStatepackages/shared/src/lib/automation/flows/operations/ — FlowOperationRequest union and all 26 op typespackages/shared/src/lib/automation/flows/actions/action.ts — FlowAction discriminated unionpackages/shared/src/lib/automation/flows/triggers/trigger.ts — FlowTrigger discriminated unionpackages/web/src/features/flows/api/flows-api.tsx — flowsApi (list, create, update, get, versions, delete, count)packages/web/src/features/flows/hooks/flow-hooks.tsx — flowHooks (status change, export, import, test, version management)packages/web/src/features/flows/components/ — FlowStatusToggle, ImportFlowDialog, ShareTemplateDialog, ChangeOwnerDialogpackages/web/src/features/flows/utils/flows-utils.tsx — download, zip, template parsing helperspackages/web/src/app/builder/index.tsx — visual flow builder entry pointpackages/web/src/app/builder/flow-canvas/ — XYFlow canvas (nodes, edges, drag layer, context menu)packages/web/src/app/builder/state/ — Zustand-based builder state (flow, run, canvas, notes, step form, piece selector)packages/web/src/app/builder/step-settings/ — step configuration panel and split/drawer layout for the test panelpackages/web/src/app/builder/test-step/ — test-panel UI (test-panel-host, action/trigger sections, sample-data viewer, view toggle, CTA buttons); test-runner-context.tsx hoists useTestAction + the webhook-return dialog so the bottom CTA can fire the test in-treepackages/web/src/app/builder/pieces-selector/ — piece/action browserpackages/web/src/app/routes/automations/index.tsx — flows list pagePOST /v1/flows/:id.LOCK_AND_PUBLISH snapshots it to LOCKED and optionally enables the flow.IMPORT_FLOW operations)./form/:flowId) or chat interface (/chat/:flowId) as their trigger UI.Flow: id, projectId, folderId (nullable), status (ENABLED/DISABLED), externalId, publishedVersionId (nullable, unique FK), metadata (JSONB), operationStatus (NONE/DELETING/ENABLING/DISABLING), timeSavedPerRun, ownerId, templateId. Relations: project, folder, owner, publishedVersion (one-to-one), versions (one-to-many), runs, events, tableWebhooks.
FlowVersion: id, flowId, displayName, schemaVersion, trigger (JSONB — full flow graph), connectionIds[], agentIds[], updatedBy, valid, state (DRAFT/LOCKED), backupFiles (JSONB), notes[] (JSONB). Relations: flow, updatedByUser.
Folder: id, projectId, displayName. Used to organize flows and tables. Case-insensitive uniqueness.
All flow modifications go through POST /v1/flows/:id with a FlowOperationRequest discriminated union. 26 operation types:
flow.publishedVersionIdWhen LOCK_AND_PUBLISH or CHANGE_STATUS to ENABLED:
When CHANGE_STATUS to DISABLED:
human-input/)GET /form/:flowId): Public endpoint returning form UI config from flow definition. Supports ?useDraft=true.GET /chat/:flowId): Public endpoint returning chat UI config. Supports sessionId, message, file attachments.step-run/)The visual builder (packages/web/src/app/builder/) uses XYFlow for the canvas. State is split into focused Zustand slices, composed by builder-state-provider.tsx:
flow-state.ts — current flow and version, pending operationsrun-state.ts — active test run, step results, focused/failed step (used by the run-info widget's "See error" affordance); setRun resets userManuallySelectedStepDuringRun whenever a new run id arrivescanvas-state.ts — viewport, selected node, drag state, plus the userManuallySelectedStepDuringRun flag and resumeLiveFollow action that gate auto-follow. The auto-focus effect lives in useFocusOnStep (flow-canvas/hooks.tsx): it calls selectStepByName(step, { fromAutoFocus: true }) to pan the canvas to the latest engine step, and short-circuits whenever userManuallySelectedStepDuringRun is set. The flag flips to true when the user picks a different step mid-run (any selectStepByName call without fromAutoFocus) and clears via resumeLiveFollow or when setRun receives a new run id. Also owns the test-panel layout state: testPanelView ('split' | 'drawer', persisted via localStorage) and isTestPanelOpenstep-form-state.ts — open/focused step configurationpiece-selector-state.ts — piece browser visibility and searchnotes-state.tsx — sticky notes overlaychat-state.ts — embedded chat drawer state for testing chat_submission-trigger flows from the builderflowHooks.useChangeFlowStatus handles both publish and enable/disable, surfaces TRIGGER_UPDATE_STATUS errors via an ApErrorDialog, and maps gateway timeout errors to a user-readable message. flowHooks.importFlowsFromTemplates replaces externalId references across a multi-flow template import to maintain cross-flow links.
The step-settings sidebar hosts a test panel with two layouts, switched via testPanelView in canvas state:
drawer — slides up from the bottom of the sidebar, occupies 60% height, sits at z-50 over the settings form. Dismisses on outside-click (pointerdown listener in test-panel-host.tsx, ignoring Radix poppers, role="dialog", and resizable handles).split — non-resizable 50/50 horizontal split between settings form and test panel inside the sidebar.step-settings/index.tsx composes the two layouts via StepSettingsLayout. The bottom CTA (TestStepCTAButton) shows under the settings form when the drawer is closed; clicking it opens the drawer and auto-fires the test by calling useActionTestRunner().fireTest() or useTriggerTestRunner().fireTest() from context. ActionTestRunnerProvider owns the action mutation and the return-response webhook dialog; TriggerTestRunnerProvider owns the trigger piece lookup, the three trigger mutations (simulate, poll, saveMock), the MCP-tool testing dialog, and a fireTest() dispatcher that picks the right one based on triggerEventUtils.getTestType. Both providers are wired with the Zustand-backed selectedStep (not the RHF form values) so step.valid stays in sync with the resolver-computed validity that applyOperation writes to canvas state — RHF never writes the resolver's valid back into its own form store, so reading from form.getValues() / form.formState.isValid would observe a stale value for freshly-added steps.
builder/index.tsx drives the right-sidebar pixel size imperatively: a useLayoutEffect on react-resizable-panels' PanelImperativeHandle.resize() targets 1000px (initial split open), 850px (subsequent split open), or 25% (drawer). A separate useEffect attaches a ResizeObserver while the user drags the handle and auto-collapses split → drawer once the sidebar drops under 700px. The old useAnimateSidebar hook has been removed in favour of this approach.