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

nametyperequiredconstraintsdescription
branchesarrayyesitems: BranchOrdered list of conditional branches. Each branch is evaluated sequentially until a condition matches.

Branch

Each branch defines a conditional path in the workflow.

nametyperequiredconstraintsdescription
labelstringnoOptional human-readable identifier for the branch (e.g., "High Value Customer", "Invalid Input").
targetstringnoOptional identifier of the workflow step to execute when this branch's condition evaluates to true.
conditionConditionyesunion: binaryRule, unaryRule, or groupThe 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

Compares two operands using a binary operator. Supports both numeric and string comparisons.

nametyperequiredconstraintsdescription
typestringyesenum: binaryRuleDiscriminator for binary rule variant.
binaryOperatorstringyesenum: see BinaryOperator table belowThe comparison operator to apply.
leftOperandstringyesLeft-hand side of the comparison (typically a workflow variable reference or literal).
rightOperandstringyesRight-hand side of the comparison (typically a workflow variable reference or literal).

Condition variant: unaryRule

Evaluates a single operand for emptiness.

nametyperequiredconstraintsdescription
typestringyesenum: unaryRuleDiscriminator for unary rule variant.
unaryOperatorstringyesenum: IS_EMPTY, IS_NOT_EMPTYThe operator to apply to the operand.
operandstringyesThe value to check for emptiness (typically a workflow variable reference).

Condition variant: group

Combines multiple conditions using a logical operator. Groups support recursive nesting.

nametyperequiredconstraintsdescription
typestringyesenum: groupDiscriminator for group variant.
logicOperatorstringyesenum: AND, ORLogical operator applied to all conditions in the group. AND requires all conditions to be true; OR requires at least one to be true.
conditionsarrayyesitems: Condition; minItems: 1Nested 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.

OperatorApplies toDescription
EQUALnumeric, stringLeft equals right.
NOT_EQUALnumeric, stringLeft does not equal right.
GREATER_THANnumericLeft is greater than right.
LESS_THANnumericLeft is less than right.
GREATER_THAN_OR_EQUALnumericLeft is greater than or equal to right.
LESS_THAN_OR_EQUALnumericLeft is less than or equal to right.
CONTAINSstringLeft contains the substring right.
NOT_CONTAINSstringLeft does not contain the substring right.
STARTS_WITHstringLeft starts with right.
DOES_NOT_START_WITHstringLeft does not start with right.
ENDS_WITHstringLeft ends with right.
DOES_NOT_END_WITHstringLeft does not end with right.
IS_LONGER_THANstringLength of left is greater than length of right.
IS_NOT_LONGER_THANstringLength of left is not greater than length of right.
IS_SHORTER_THANstringLength of left is less than length of right.
IS_NOT_SHORTER_THANstringLength of left is not less than length of right.
IS_LONGER_OR_EQUALstringLength of left is greater than or equal to length of right.
IS_NOT_LONGER_OR_EQUALstringLength of left is not greater than or equal to length of right.
IS_SHORTER_OR_EQUALstringLength of left is less than or equal to length of right.
IS_NOT_SHORTER_OR_EQUALstringLength of left is not less than or equal to length of right.

UnaryOperator enum

OperatorDescription
IS_EMPTYOperand is null, undefined, or empty string.
IS_NOT_EMPTYOperand is not null, undefined, or empty string.

LogicOperator enum

OperatorDescription
ANDAll conditions in the group must evaluate to true.
ORAt 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"
              }
            ]
          }
        ]
      }
    }
  ]
}