Publishing a workflow makes it ready for production use. This guide covers the publishing process, testing strategies, and managing workflow states.
Understanding Flow States
Workflows in Activepieces have two key state dimensions:
Version State
// From packages/shared/src/lib/automation/flows/flow-version.ts
export enum FlowVersionState {
LOCKED = 'LOCKED', // Published version, immutable
DRAFT = 'DRAFT' // Editable version
}
Draft
Editable version where you make changes. Not running in production.
Locked
Published version that’s immutable and running in production.
Flow Status
// From packages/shared/src/lib/automation/flows/flow.ts
export enum FlowStatus {
ENABLED = 'ENABLED', // Actively running
DISABLED = 'DISABLED' // Paused
}
A flow must be published (have a locked version) before it can be enabled.
Publishing Your First Flow
Complete Your Workflow
Ensure all steps are configured and valid:// From packages/shared/src/lib/automation/flows/flow-version.ts
{
"flowVersion": {
"valid": true, // All steps must be valid
"state": "DRAFT",
"trigger": { /* configured */ },
// ... actions
}
}
You cannot publish an invalid flow. Fix all validation errors first.
Test Your Flow
Click Test Flow to run it with sample data:// From packages/web/src/app/builder/flow-canvas/widgets/test-flow-widget.tsx
const { mutate: runFlow } = flowHooks.useTestFlowOrStartManualTrigger({
flowVersionId: flowVersion.id,
onUpdateRun: (response) => {
// View execution results
}
});
Verify:
- All steps execute successfully
- Data flows correctly between steps
- Output matches expectations
Save Changes
Activepieces auto-saves as you work, but ensure all changes are saved:// Changes are automatically saved
// Wait for "Saving..." indicator to complete
Click Publish
Click the Publish button at the top of the canvas:// From packages/web/src/app/builder/flow-canvas/widgets/publish-flow-reminder-widget.tsx
const { mutateAsync: publish } = flowHooks.useChangeFlowStatus({
flowId: flow.id,
change: 'publish',
onSuccess: (response) => {
setFlow(response.flow);
setVersion(response.flow.version);
}
});
This creates a locked version and sets it as published. Enable the Flow
After publishing, toggle the flow status to Enabled:// From packages/web/src/features/flows/components/flow-status-toggle.tsx
const { mutate: changeStatus } = flowHooks.useChangeFlowStatus({
flowId: flow.id,
change: FlowStatus.ENABLED,
onSuccess: (response) => {
// Flow is now running!
}
});
When you have unpublished changes, you’ll see a widget at the top of the canvas:
// From packages/web/src/app/builder/flow-canvas/widgets/publish-flow-reminder-widget.tsx
<PublishFlowReminderWidget>
<div>
"You have unpublished changes"
<Button onClick={() => discardChange()}>
Discard changes
</Button>
<Button onClick={() => publish()} disabled={!isValid}>
Publish
</Button>
</div>
</PublishFlowReminderWidget>
Creates a new locked version with your changes.
Reverts your draft to match the currently published version.
Flow Status Toggle
The status toggle controls whether a flow is actively running:
// From packages/web/src/features/flows/components/flow-status-toggle.tsx
<Switch
checked={isFlowPublished}
onCheckedChange={() => changeStatus()}
disabled={
isLoading ||
!userHasPermissionToToggleFlowStatus ||
isNil(flow.publishedVersionId) // Must publish first!
}
/>
Enabled
Flow is active and will execute when triggered.
Disabled
Flow is paused and won’t execute.
The toggle is disabled until you publish the flow for the first time.
Testing Strategies
Test Individual Steps
You can test steps one at a time:
Configure Trigger
Set up your trigger with sample data or test credentials.
Test Trigger
Click the test button on the trigger to generate sample data.
Add First Action
Add and configure the first action.
Test Action
Test the action using the trigger’s sample data.
Repeat
Continue adding and testing actions one by one.
Test Complete Flow
// From packages/web/src/app/builder/flow-canvas/widgets/test-flow-widget.tsx
const TestFlowWidget = () => {
const triggerHasSampleData =
flowVersion.trigger.type === FlowTriggerType.PIECE &&
!isNil(flowVersion.trigger.settings.sampleData?.lastTestDate);
return (
<AboveTriggerButton
onClick={() => runFlow()}
text={isManualTrigger ? 'Run Flow' : 'Test Flow'}
disable={!triggerHasSampleData && !isManualTrigger}
loading={isTestingFlow}
/>
);
};
The Test Flow button is disabled until your trigger has sample data.
Testing Environments
// From packages/shared/src/lib/automation/flow-run/flow-run.ts
export enum RunEnvironment {
PRODUCTION = 'PRODUCTION', // Published, enabled flows
TESTING = 'TESTING' // Test runs
}
Testing Environment
Production Environment
- Triggered manually via “Test Flow” button
- Uses sample data
- Doesn’t affect production data
- Logged separately from production runs
- Triggered by actual events (webhooks, schedules)
- Uses real data
- Affects production systems
- Counted towards usage limits
Version Management
Every time you publish, a new locked version is created:
// From packages/server/api/src/app/flows/flow-version/flow-version.service.ts
export type FlowVersion = {
id: string,
flowId: string,
displayName: string,
trigger: FlowTrigger,
valid: boolean,
state: FlowVersionState.DRAFT | FlowVersionState.LOCKED,
created: string,
updated: string,
updatedBy: string | null
}
Lock Piece Versions
When publishing, piece versions are locked:
// From packages/server/api/src/app/flows/flow-version/flow-version.service.ts
async lockPieceVersions(flowVersion: FlowVersion): Promise<FlowVersion> {
const pieceVersion: Record<string, string> = {};
const steps = flowStructureUtil.getAllSteps(flowVersion.trigger);
for (const step of steps) {
if ([FlowActionType.PIECE, FlowTriggerType.PIECE].includes(step.type)) {
const pieceMetadata = await pieceMetadataService.getOrThrow({
name: step.settings.pieceName,
version: step.settings.pieceVersion
});
pieceVersion[step.name] = pieceMetadata.version;
}
}
// Lock to specific versions
return transferFlow(flowVersion, (step) => {
if (pieceVersion[step.name]) {
step.settings.pieceVersion = pieceVersion[step.name];
}
return step;
});
}
Locking piece versions ensures your published workflow always uses the same piece versions, preventing breaking changes.
Publishing Workflow
Apply Operation
// From packages/server/api/src/app/flows/flow-version/flow-version.service.ts
async applyOperation({
flowVersion,
projectId,
userId,
userOperation,
entityManager
}) {
let operations: FlowOperationRequest[] = [];
switch (userOperation.type) {
case FlowOperationType.LOCK_FLOW:
// Lock piece versions first
mutatedFlowVersion = await this.lockPieceVersions({
projectId,
flowVersion: mutatedFlowVersion,
entityManager
});
operations = [userOperation];
break;
// ... other operations
}
// Update metadata
mutatedFlowVersion.updated = dayjs().toISOString();
mutatedFlowVersion.updatedBy = userId;
return flowVersionRepo.save(mutatedFlowVersion);
}
Enabling and Disabling Flows
Flow Status API
// From packages/shared/src/lib/automation/flows/flow.ts
export type Flow = {
id: string,
projectId: string,
status: FlowStatus.ENABLED | FlowStatus.DISABLED,
publishedVersionId: string | null,
operationStatus: FlowOperationStatus // ENABLING, DISABLING, etc.
}
Operation Status
export enum FlowOperationStatus {
NONE = 'NONE',
DELETING = 'DELETING',
ENABLING = 'ENABLING',
DISABLING = 'DISABLING'
}
Flows show intermediate status while enabling/disabling:
User Toggles Status
User clicks the toggle to enable/disable.
Status Changes to ENABLING/DISABLING
The operationStatus shows the operation in progress.
System Updates Triggers
Background jobs register/unregister webhooks, schedules, etc.
Status Changes to NONE
Operation completes, flow is now enabled/disabled.
Pre-Flight Checks
Before publishing, ensure:
flowVersion.valid === true
Each step must be properly configured with:
- Required fields filled
- Valid connections selected
- Proper data mappings
Always test your flow before publishing:
- Run with sample data
- Check all step outputs
- Verify final result
Ensure all connections used by the flow:
- Are properly authenticated
- Have necessary permissions
- Are not expired
If using external APIs:
- Check rate limits
- Consider batch sizes
- Plan for peak usage
Rolling Back Changes
If you published changes but need to revert:
Method 1: Discard Draft Changes
// From packages/web/src/app/builder/flow-canvas/widgets/publish-flow-reminder-widget.tsx
const { mutate: discardChange } = useMutation({
mutationFn: async () => {
if (!flow.publishedVersionId) return;
// Overwrite draft with published version
await overWriteDraftWithVersion({
flowId: flow.id,
versionId: flow.publishedVersionId
});
await publish();
}
});
This reverts your draft to the currently published version.
Method 2: Publish Previous Version
View Version History
Click on the version selector to see all versions.
Select Previous Version
Choose the version you want to restore.
Use as Draft
Click “Use as Draft” to copy it to your draft.
Publish
Publish the restored version.
// From packages/server/api/src/app/flows/flow-version/flow-version.service.ts
case FlowOperationType.USE_AS_DRAFT: {
const previousVersion = await flowVersionService.getFlowVersionOrThrow({
flowId: flowVersion.flowId,
versionId: userOperation.request.versionId,
removeConnectionsName: false
});
operations = [{
type: FlowOperationType.IMPORT_FLOW,
request: {
trigger: previousVersion.trigger,
displayName: previousVersion.displayName,
schemaVersion: previousVersion.schemaVersion,
notes: previousVersion.notes
}
}];
break;
}
Best Practices
Test Thoroughly Before Publishing
- Test with real-world data samples
- Test edge cases and error scenarios
- Verify all integrations work
- Check performance with expected load
Use Descriptive Version Names
Add notes or comments to explain what changed in each version:"Added error handling to API calls"
"Fixed data mapping for user emails"
For critical workflows:
- Publish the new version
- Keep old version enabled
- Test new version manually
- Switch to new version
- Monitor for issues
- Check run history regularly
- Set up error notifications
- Review execution logs
- Track success rates
- Don’t leave unfinished changes in draft
- Publish or discard regularly
- Use version history to track changes
Publishing Checklist
✅ All Steps Valid
Every step shows green validation indicator.
✅ Flow Tested
Test run completed successfully.
✅ Connections Active
All required connections are authenticated.
✅ Error Handling Configured
Critical steps have retry/continue on failure set.
✅ Changes Saved
No unsaved changes indicator.
✅ Click Publish
Publish button clicked and confirmed.
✅ Enable Flow
Toggle switched to enabled.
✅ Verify Running
Flow shows as active/enabled in list.
Troubleshooting
Toggle Disabled After Publishing
Reason: Flow publishing operation still in progress.Solution: Wait a few seconds for operationStatus to become NONE.
Flow Not Executing After Enabling
Possible causes:
- Trigger not properly configured
- Webhook not registered
- Schedule not created
Solution:
- Disable and re-enable the flow
- Check trigger configuration
- Review trigger logs
Test Flow Button Disabled
Reason: Trigger doesn’t have sample data.Solution: Test the trigger first to generate sample data.
Next Steps
Versioning
Learn about version management
Debugging
Debug production issues
Monitoring
Monitor workflow performance
Best Practices
Workflow best practices