Skip to main content

Basic Usage

import { Workflow } from '@frontmcp/sdk';

@Workflow({
  name: 'data-pipeline',
  description: 'Extract, transform, and load data',
  steps: [
    { id: 'extract', jobName: 'extract-data' },
    { id: 'transform', jobName: 'transform-data', dependsOn: ['extract'] },
    { id: 'load', jobName: 'load-data', dependsOn: ['transform'] },
  ],
})
class DataPipelineWorkflow {}
Workflows are declarative — the class body is empty. All configuration goes in the decorator metadata.

Signature

function Workflow(opts: WorkflowMetadata): ClassDecorator

Configuration Options

Required Properties

PropertyTypeDescription
namestringUnique workflow identifier
stepsWorkflowStep[]Step definitions (minimum 1)

Optional Properties

PropertyTypeDefaultDescription
descriptionstringWorkflow description
idstringnameStable identifier for tracking
trigger'manual' | 'webhook' | 'event''manual'How the workflow is triggered
webhookWorkflowWebhookConfigWebhook configuration
timeoutnumber600000Max total execution time in ms (10 min)
maxConcurrencynumber5Max parallel step concurrency
tagsstring[]Categorization tags
labelsRecord<string, string>Key-value labels
hideFromDiscoverybooleanfalseHide from list-workflows
permissionsJobPermission[]RBAC permission rules
inputSchemaZodShapeWorkflow-level input schema
outputSchemaZodShapeWorkflow-level output schema

WorkflowStep

PropertyTypeDefaultDescription
idstringRequired. Unique step identifier
jobNamestringRequired. Name of the registered job to execute
dependsOnstring[][]Step IDs that must complete first
inputobject | functionWorkflow inputStatic input or dynamic callback
condition(steps) => booleanSkip step if returns false
continueOnErrorbooleanfalseContinue workflow if step fails
timeoutnumberJob defaultPer-step timeout override in ms
retryJobRetryConfigJob defaultPer-step retry override (maxAttempts, backoffMs, backoffMultiplier, maxBackoffMs)

WorkflowWebhookConfig

PropertyTypeDefaultDescription
pathstring/workflows/webhook/{name}Custom webhook path
secretstringWebhook secret for validation. Load from environment variables or a secrets manager — never hardcode in source.
methods('GET' | 'POST')[]['POST']Allowed HTTP methods

Examples

Linear Workflow

@Workflow({
  name: 'onboarding',
  description: 'User onboarding pipeline',
  steps: [
    { id: 'create-account', jobName: 'create-account' },
    {
      id: 'send-welcome',
      jobName: 'send-email',
      dependsOn: ['create-account'],
      input: (steps) => ({
        to: steps.get('create-account').outputs.email,
        subject: 'Welcome!',
        body: 'Welcome to our platform.',
      }),
    },
    {
      id: 'setup-defaults',
      jobName: 'setup-defaults',
      dependsOn: ['create-account'],
      input: (steps) => ({
        userId: steps.get('create-account').outputs.userId,
      }),
    },
  ],
})
class OnboardingWorkflow {}

Parallel Workflow

@Workflow({
  name: 'multi-analysis',
  description: 'Run multiple analyses in parallel',
  maxConcurrency: 3,
  steps: [
    { id: 'fetch', jobName: 'fetch-data' },
    { id: 'sentiment', jobName: 'analyze-sentiment', dependsOn: ['fetch'] },
    { id: 'entities', jobName: 'extract-entities', dependsOn: ['fetch'] },
    { id: 'topics', jobName: 'detect-topics', dependsOn: ['fetch'] },
    {
      id: 'report',
      jobName: 'generate-report',
      dependsOn: ['sentiment', 'entities', 'topics'],
      input: (steps) => ({
        sentiment: steps.get('sentiment').outputs,
        entities: steps.get('entities').outputs,
        topics: steps.get('topics').outputs,
      }),
    },
  ],
})
class MultiAnalysisWorkflow {}

Conditional Workflow

@Workflow({
  name: 'conditional-pipeline',
  description: 'Pipeline with conditional steps',
  steps: [
    { id: 'validate', jobName: 'validate-input' },
    {
      id: 'premium-processing',
      jobName: 'premium-process',
      dependsOn: ['validate'],
      condition: (steps) =>
        steps.get('validate').outputs.tier === 'premium',
    },
    {
      id: 'standard-processing',
      jobName: 'standard-process',
      dependsOn: ['validate'],
      condition: (steps) =>
        steps.get('validate').outputs.tier !== 'premium',
    },
    {
      id: 'finalize',
      jobName: 'finalize',
      dependsOn: ['premium-processing', 'standard-processing'],
    },
  ],
})
class ConditionalPipelineWorkflow {}
A step whose condition returns false is treated as skipped and satisfies downstream dependsOn dependencies. This means steps that depend on a skipped step will still run.

Function-Based Alternative

import { workflow } from '@frontmcp/sdk';

const DataPipeline = workflow({
  name: 'data-pipeline',
  steps: [
    { id: 'extract', jobName: 'extract-data' },
    { id: 'transform', jobName: 'transform-data', dependsOn: ['extract'] },
    { id: 'load', jobName: 'load-data', dependsOn: ['transform'] },
  ],
});

WorkflowRegistry

Workflow registry API

Workflows Guide

Workflows documentation

@Job

Define jobs

DirectClient

Programmatic execution