Condition Step
Branch workflow execution based on expressions
The condition step evaluates an expression against the current workflow state and branches execution to one of two named steps. This is how you build if/else logic into your workflows.
Schema
| Field | Type | Required | Description |
|---|---|---|---|
type | "condition" | Yes | Step type discriminator |
name | string (1-100 chars) | Yes | Unique step name within the workflow |
expression | string | Yes | Dot-notation path into workflow state to evaluate |
trueStep | string | Yes | Step name to jump to when the expression is truthy |
falseStep | string | Yes | Step name to jump to when the expression is falsy |
retry | RetryPolicy | No | Retry policy for failed executions |
Configuration Example
{
"type": "condition",
"name": "check-lead-quality",
"expression": "leadScore",
"trueStep": "send-personalized-email",
"falseStep": "add-to-nurture-list"
}In this example, the engine reads workflowState.leadScore. If it is truthy (non-zero, non-null, non-empty), execution jumps to send-personalized-email. Otherwise, it jumps to add-to-nurture-list.
Expression Evaluation
Expressions use dot-notation to traverse the workflow state object. The engine splits the expression on . and walks the state tree:
// Expression: "enrichedLead.company.size"
// Evaluates: workflowState.enrichedLead.company.size
const parts = expression.split('.');
let value = workflowState;
for (const part of parts) {
value = value[part];
}
const result = Boolean(value);Truthiness rules
The expression result is coerced to a boolean using JavaScript Boolean():
| Value | Result |
|---|---|
| Non-zero number | true |
| Non-empty string | true |
| Non-empty object / array | true |
0, "", null, undefined | false |
Nested paths
You can reach into deeply nested state objects:
{
"type": "condition",
"name": "check-approval-status",
"expression": "reviewResult.approved",
"trueStep": "deploy-changes",
"falseStep": "notify-rejection"
}Branching with __nextStep
When a condition step completes, it does not advance to the next step in definition order. Instead, it sets a __nextStep key in the workflow state that tells the engine which step to execute next.
Internal mechanics
-
The engine evaluates the expression and determines the branch:
const branchResult = Boolean(value); return { status: 'completed', output: { evaluated: branchResult, nextStep: branchResult ? step.trueStep : step.falseStep, }, stateUpdates: { __condition_checkLeadQuality: branchResult, __nextStep: branchResult ? step.trueStep : step.falseStep, }, }; -
The engine merges
stateUpdatesinto the workflow state, which now includes__nextStep. -
When resolving the next step, the engine checks for
__nextStepfirst:if (typeof state['__nextStep'] === 'string') { return state['__nextStep']; } -
After the step is completed,
__nextStepis cleared from the persisted state to avoid affecting future step resolution. -
The branch evaluation is also recorded as
__condition_{stepName}in state for observability.
Output
The condition step persists its evaluation result in the step run output:
{
"evaluated": true,
"nextStep": "send-personalized-email"
}Full Workflow Example
{
"name": "lead-routing",
"version": "1.0",
"steps": [
{
"type": "task",
"name": "score-lead",
"handler": "scoreLead"
},
{
"type": "condition",
"name": "check-score",
"expression": "leadScore",
"trueStep": "assign-to-sales",
"falseStep": "add-to-nurture"
},
{
"type": "task",
"name": "assign-to-sales",
"handler": "assignToSalesRep"
},
{
"type": "task",
"name": "add-to-nurture",
"handler": "addToNurtureCampaign"
}
]
}In this workflow, the score-lead task sets stateUpdates: { leadScore: 85 }. The condition step reads leadScore, finds it truthy, and jumps to assign-to-sales. The add-to-nurture step is skipped entirely.
Note: Both branch targets (trueStep and falseStep) must reference step names that exist in the workflow definition. If a referenced step name is not found, the engine will fail the workflow with "Step '{name}' not found in definition".