Skip to main content
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

1

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.
2

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
3

Save Changes

Activepieces auto-saves as you work, but ensure all changes are saved:
// Changes are automatically saved
// Wait for "Saving..." indicator to complete
4

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.
5

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!
  }
});

The Publishing Widget

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.

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:
1

Configure Trigger

Set up your trigger with sample data or test credentials.
2

Test Trigger

Click the test button on the trigger to generate sample data.
3

Add First Action

Add and configure the first action.
4

Test Action

Test the action using the trigger’s sample data.
5

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
}
  • Triggered manually via “Test Flow” button
  • Uses sample data
  • Doesn’t affect production data
  • Logged separately from production runs

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:
1

User Toggles Status

User clicks the toggle to enable/disable.
2

Status Changes to ENABLING/DISABLING

The operationStatus shows the operation in progress.
3

System Updates Triggers

Background jobs register/unregister webhooks, schedules, etc.
4

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
flowVersion.trigger.type !== FlowTriggerType.EMPTY
flowVersion.trigger.valid === true
Your trigger must be:
  • Fully configured
  • Have required authentication
  • Pass validation checks
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

1

View Version History

Click on the version selector to see all versions.
2

Select Previous Version

Choose the version you want to restore.
3

Use as Draft

Click “Use as Draft” to copy it to your draft.
4

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 with real-world data samples
  • Test edge cases and error scenarios
  • Verify all integrations work
  • Check performance with expected load
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:
  1. Publish the new version
  2. Keep old version enabled
  3. Test new version manually
  4. Switch to new version
  5. 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

1

✅ All Steps Valid

Every step shows green validation indicator.
2

✅ Flow Tested

Test run completed successfully.
3

✅ Connections Active

All required connections are authenticated.
4

✅ Error Handling Configured

Critical steps have retry/continue on failure set.
5

✅ Changes Saved

No unsaved changes indicator.
6

✅ Click Publish

Publish button clicked and confirmed.
7

✅ Enable Flow

Toggle switched to enabled.
8

✅ Verify Running

Flow shows as active/enabled in list.

Troubleshooting

Reasons:
  • Flow has validation errors
  • No changes to publish
  • Currently saving
Solution: Fix validation errors shown in red on steps.
Reason: Flow publishing operation still in progress.Solution: Wait a few seconds for operationStatus to become NONE.
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
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