Logic Step
Enables conditional workflow branching based on rule evaluation. Use this step to conditionally route workflow execution based on evaluated rules. The Logic step supports binary comparisons (equality, numeric ranges, string operations), unary checks (emptiness), and nested logical grouping with AND/OR operators.
Top-level properties
| name | type | required | constraints | description |
|---|---|---|---|---|
branches | array | yes | items: Branch | Ordered list of conditional branches. Each branch is evaluated sequentially until a condition matches. |
Branch
Each branch defines a conditional path in the workflow.
| name | type | required | constraints | description |
|---|---|---|---|---|
label | string | no | Optional human-readable identifier for the branch (e.g., "High Value Customer", "Invalid Input"). | |
target | string | no | Optional identifier of the workflow step to execute when this branch's condition evaluates to true. | |
condition | Condition | yes | union: binaryRule, unaryRule, or group | The rule or group of rules that must evaluate to true for this branch to be taken. |
Condition
A condition is a discriminated union representing a single rule or a logical group. All conditions have a type discriminator field.
Condition variant: binaryRule
binaryRuleCompares two operands using a binary operator. Supports both numeric and string comparisons.
| name | type | required | constraints | description |
|---|---|---|---|---|
type | string | yes | enum: binaryRule | Discriminator for binary rule variant. |
binaryOperator | string | yes | enum: see BinaryOperator table below | The comparison operator to apply. |
leftOperand | string | yes | Left-hand side of the comparison (typically a workflow variable reference or literal). | |
rightOperand | string | yes | Right-hand side of the comparison (typically a workflow variable reference or literal). |
Condition variant: unaryRule
unaryRuleEvaluates a single operand for emptiness.
| name | type | required | constraints | description |
|---|---|---|---|---|
type | string | yes | enum: unaryRule | Discriminator for unary rule variant. |
unaryOperator | string | yes | enum: IS_EMPTY, IS_NOT_EMPTY | The operator to apply to the operand. |
operand | string | yes | The value to check for emptiness (typically a workflow variable reference). |
Condition variant: group
groupCombines multiple conditions using a logical operator. Groups support recursive nesting.
| name | type | required | constraints | description |
|---|---|---|---|---|
type | string | yes | enum: group | Discriminator for group variant. |
logicOperator | string | yes | enum: AND, OR | Logical operator applied to all conditions in the group. AND requires all conditions to be true; OR requires at least one to be true. |
conditions | array | yes | items: Condition; minItems: 1 | Nested array of conditions (binaryRule, unaryRule, or group). |
BinaryOperator enum
Binary operators support different data types. String-only operators will fail if used on numeric operands, and vice versa.
| Operator | Applies to | Description |
|---|---|---|
EQUAL | numeric, string | Left equals right. |
NOT_EQUAL | numeric, string | Left does not equal right. |
GREATER_THAN | numeric | Left is greater than right. |
LESS_THAN | numeric | Left is less than right. |
GREATER_THAN_OR_EQUAL | numeric | Left is greater than or equal to right. |
LESS_THAN_OR_EQUAL | numeric | Left is less than or equal to right. |
CONTAINS | string | Left contains the substring right. |
NOT_CONTAINS | string | Left does not contain the substring right. |
STARTS_WITH | string | Left starts with right. |
DOES_NOT_START_WITH | string | Left does not start with right. |
ENDS_WITH | string | Left ends with right. |
DOES_NOT_END_WITH | string | Left does not end with right. |
IS_LONGER_THAN | string | Length of left is greater than length of right. |
IS_NOT_LONGER_THAN | string | Length of left is not greater than length of right. |
IS_SHORTER_THAN | string | Length of left is less than length of right. |
IS_NOT_SHORTER_THAN | string | Length of left is not less than length of right. |
IS_LONGER_OR_EQUAL | string | Length of left is greater than or equal to length of right. |
IS_NOT_LONGER_OR_EQUAL | string | Length of left is not greater than or equal to length of right. |
IS_SHORTER_OR_EQUAL | string | Length of left is less than or equal to length of right. |
IS_NOT_SHORTER_OR_EQUAL | string | Length of left is not less than or equal to length of right. |
UnaryOperator enum
| Operator | Description |
|---|---|
IS_EMPTY | Operand is null, undefined, or empty string. |
IS_NOT_EMPTY | Operand is not null, undefined, or empty string. |
LogicOperator enum
| Operator | Description |
|---|---|
AND | All conditions in the group must evaluate to true. |
OR | At least one condition in the group must evaluate to true. |
Examples
Minimal example: Single binary rule
TypeScript
import { newLogicStepConfig, type NewLogicStepConfig } from 'odin-workflows-schema';
const config: NewLogicStepConfig = {
branches: [
{
condition: {
type: 'group',
logicOperator: 'AND',
conditions: [
{
type: 'group',
logicOperator: 'OR',
conditions: [
{
type: 'binaryRule',
binaryOperator: 'EQUAL',
leftOperand: "{{`User`.`status`}}",
rightOperand: 'active',
},
],
},
]
},
},
],
};
const result = newLogicStepConfig.safeParse(config);
if (result.success) {
console.log('Valid Logic step configuration');
} else {
console.error('Validation errors:', result.error);
}JSON
{
"branches": [
{
"condition": {
"type": "group",
"logicOperator": "AND",
"conditions": [
{
"type": "group",
"logicOperator": "OR",
"conditions": [
{
"type": "binaryRule",
"binaryOperator": "EQUAL",
"leftOperand": "{{`User`.`status`}}",
"rightOperand": "active"
}
]
}
]
}
}
]
}Minimal example: Unary rule
TypeScript
import { newLogicStepConfig } from 'odin-workflows-schema';
const config = {
branches: [
{
label: 'Check if email is empty',
condition: {
type: 'group',
logicOperator: 'AND',
conditions: [
{
type: 'group',
logicOperator: 'OR',
conditions: [
{
type: 'unaryRule',
unaryOperator: 'IS_EMPTY',
operand: '{{`user`.`email`}}',
}
],
}
],
},
},
],
};
const result = newLogicStepConfig.safeParse(config);JSON
{
"branches": [
{
"label": "Check if email is empty",
"condition": {
"type": "group",
"logicOperator": "AND",
"conditions": [
{
"type": "group",
"logicOperator": "OR",
"conditions": [
{
"type": "unaryRule",
"unaryOperator": "IS_EMPTY",
"operand": "{{`user`.`email`}}"
}
]
}
]
}
}
]
}Full example: Complex nested logic with multiple branches
This example demonstrates multiple branches with nested groups, numeric comparisons, string operations, and logical operators.
TypeScript
import { newLogicStepConfig, type NewLogicStepConfig } from 'odin-workflows-schema';
const config: NewLogicStepConfig = {
branches: [
{
label: 'High-value active customer',
target: 'premium_flow',
condition: {
type: 'group',
logicOperator: 'AND',
conditions: [
{
type: 'group',
logicOperator: 'OR',
conditions: [
{
type: 'binaryRule',
binaryOperator: 'EQUAL',
leftOperand: "{{`User`.`status`}}",
rightOperand: 'active',
}
]
},
{
type: 'group',
logicOperator: 'OR',
conditions: [
{
type: 'binaryRule',
binaryOperator: 'GREATER_THAN_OR_EQUAL',
leftOperand: "{{`User`.`credit`}}",
rightOperand: '1000',
}
]
},
{
type: 'group',
logicOperator: 'OR',
conditions: [
{
type: 'binaryRule',
binaryOperator: 'CONTAINS',
leftOperand: "{{`User`.`kind`}}",
rightOperand: 'vip',
},
{
type: 'binaryRule',
binaryOperator: 'GREATER_THAN',
leftOperand: "{{`StepLabel`.`credit`}}",
rightOperand: '10000',
},
],
},
],
},
},
{
label: 'New customer with valid email',
target: 'onboarding_flow',
condition: {
type: 'group',
logicOperator: 'AND',
conditions: [
{
type: 'group',
logicOperator: 'OR',
conditions: [
{
type: 'binaryRule',
binaryOperator: 'EQUAL',
leftOperand: "{{`customer`.`kind`}}",
rightOperand: 'new',
},
{
type: 'unaryRule',
unaryOperator: 'IS_NOT_EMPTY',
operand: '{{`customer`.`email`}}',
},
],
}
],
},
},
{
label: 'Default fallback',
target: 'standard_flow',
condition: {
type: 'group',
logicOperator: 'AND',
conditions: [
{
type: 'group',
logicOperator: 'OR',
conditions: [
{
type: 'binaryRule',
binaryOperator: 'EQUAL',
leftOperand: '{{`user`.`isActive`}}',
rightOperand: 'true',
}
],
},
],
},
},
],
};
const result = newLogicStepConfig.safeParse(config);
if (result.success) {
console.log('Valid complex Logic step configuration');
} else {
console.error('Validation errors:', result.error);
}JSON
{
"branches": [
{
"label": "High-value active customer",
"target": "premium_flow",
"condition": {
"type": "group",
"logicOperator": "AND",
"conditions": [
{
"type": "group",
"logicOperator": "OR",
"conditions": [
{
"type": "binaryRule",
"binaryOperator": "EQUAL",
"leftOperand": "{{`User`.`status`}}",
"rightOperand": "active"
},
]
},
{
"type": "group",
"logicOperator": "OR",
"conditions": [
{
"type": "binaryRule",
"binaryOperator": "GREATER_THAN_OR_EQUAL",
"leftOperand": "{{`User`.`credit`}}",
"rightOperand": "1000"
},
]
},
{
"type": "group",
"logicOperator": "OR",
"conditions": [
{
"type": "binaryRule",
"binaryOperator": "CONTAINS",
"leftOperand": "{{`User`.`kind`}}",
"rightOperand": "vip"
},
{
"type": "binaryRule",
"binaryOperator": "GREATER_THAN",
"leftOperand": "{{`User`.`credit`}}",
"rightOperand": "10000"
}
]
}
]
}
},
{
"label": "New customer with valid email",
"target": "onboarding_flow",
"condition": {
"type": "group",
"logicOperator": "AND",
"conditions": [
{
"type": "group",
"logicOperator": "OR",
"conditions": [
{
"type": "binaryRule",
"binaryOperator": "EQUAL",
"leftOperand": "{{`customer`.`kind`}}",
"rightOperand": "new"
}
]
},
{
"type": "group",
"logicOperator": "OR",
"conditions": [
{
"type": "unaryRule",
"unaryOperator": "IS_NOT_EMPTY",
"operand": "{{`customer`.`email`}}"
}
]
}
]
}
},
{
"label": "Default fallback",
"target": "standard_flow",
"condition": {
"type": "group",
"logicOperator": "AND",
"conditions": [
{
"type": "group",
"logicOperator": "OR",
"conditions": [
{
"type": "binaryRule",
"binaryOperator": "EQUAL",
"leftOperand": "{{`user`.`isActive`}}",
"rightOperand": "true"
}
]
}
]
}
}
]
}Updated 13 days ago
