LoopFour

Condition Steps

Branch workflows based on conditional logic

Condition Steps

Condition steps enable branching logic in workflows, allowing you to execute different paths based on data values, comparisons, and complex boolean expressions.

Configuration

{
  "id": "check-amount",
  "type": "condition",
  "name": "Check Invoice Amount",
  "config": {
    "conditions": {
      "left": "{{input.amount}}",
      "operator": "gt",
      "right": "10000"
    },
    "then": ["high-value-flow"],
    "else": ["standard-flow"]
  }
}
FieldTypeRequiredDescription
idstringYesUnique step identifier
typestringYesMust be "condition"
namestringYesHuman-readable name
config.conditionsobjectYesCondition expression to evaluate
config.thenstring[]YesStep IDs to execute if true
config.elsestring[]NoStep IDs to execute if false

Simple Conditions

A simple condition compares two values using an operator:

{
  "config": {
    "conditions": {
      "left": "{{input.status}}",
      "operator": "eq",
      "right": "approved"
    },
    "then": ["process-order"],
    "else": ["send-rejection"]
  }
}
FieldDescription
leftLeft operand (template expression or literal)
operatorComparison operator
rightRight operand (optional for some operators)

Comparison Operators

OperatorDescriptionExample
eqEqual to"status" eq "active"
neNot equal to"type" ne "draft"
gtGreater than"amount" gt "1000"
gteGreater than or equal"count" gte "10"
ltLess than"priority" lt "5"
lteLess than or equal"score" lte "100"
containsString contains substring"email" contains "@company.com"
startsWithString starts with"id" startsWith "cus_"
endsWithString ends with"filename" endsWith ".pdf"
existsValue exists (not null/undefined)"customer_id" exists
isEmptyValue is empty"notes" isEmpty
matchesRegex pattern match"phone" matches "^\+1"

Operator Examples

Numeric Comparison:

{
  "conditions": {
    "left": "{{input.order_total}}",
    "operator": "gte",
    "right": "100"
  }
}

String Comparison:

{
  "conditions": {
    "left": "{{input.email}}",
    "operator": "endsWith",
    "right": "@enterprise.com"
  }
}

Existence Check:

{
  "conditions": {
    "left": "{{input.coupon_code}}",
    "operator": "exists"
  }
}

Regex Match:

{
  "conditions": {
    "left": "{{input.phone}}",
    "operator": "matches",
    "right": "^\\+1[0-9]{10}$"
  }
}

Compound Conditions

AND Conditions

All conditions must be true:

{
  "config": {
    "conditions": {
      "operator": "and",
      "conditions": [
        {
          "left": "{{input.status}}",
          "operator": "eq",
          "right": "active"
        },
        {
          "left": "{{input.amount}}",
          "operator": "gte",
          "right": "1000"
        }
      ]
    },
    "then": ["premium-processing"],
    "else": ["standard-processing"]
  }
}

OR Conditions

At least one condition must be true:

{
  "config": {
    "conditions": {
      "operator": "or",
      "conditions": [
        {
          "left": "{{input.priority}}",
          "operator": "eq",
          "right": "urgent"
        },
        {
          "left": "{{input.vip}}",
          "operator": "eq",
          "right": "true"
        }
      ]
    },
    "then": ["fast-track"],
    "else": ["normal-queue"]
  }
}

NOT Conditions

Negate a condition:

{
  "config": {
    "conditions": {
      "not": {
        "left": "{{input.status}}",
        "operator": "eq",
        "right": "cancelled"
      }
    },
    "then": ["process"],
    "else": ["skip"]
  }
}

Nested Conditions

Combine AND, OR, and NOT for complex logic:

{
  "config": {
    "conditions": {
      "operator": "and",
      "conditions": [
        {
          "left": "{{input.status}}",
          "operator": "eq",
          "right": "active"
        },
        {
          "operator": "or",
          "conditions": [
            {
              "left": "{{input.amount}}",
              "operator": "gt",
              "right": "10000"
            },
            {
              "left": "{{input.customer_tier}}",
              "operator": "eq",
              "right": "enterprise"
            }
          ]
        }
      ]
    },
    "then": ["priority-handling"],
    "else": ["standard-handling"]
  }
}

Branching Patterns

If/Else

Basic two-way branching:

{
  "id": "check-approval",
  "type": "condition",
  "config": {
    "conditions": {
      "left": "{{input.amount}}",
      "operator": "gt",
      "right": "5000"
    },
    "then": ["require-approval"],
    "else": ["auto-approve"]
  }
}

Multiple Then Steps

Execute multiple steps when condition is true:

{
  "config": {
    "conditions": {
      "left": "{{input.new_customer}}",
      "operator": "eq",
      "right": "true"
    },
    "then": ["send-welcome-email", "create-onboarding-task", "notify-sales"],
    "else": ["update-existing-record"]
  }
}

Chained Conditions (If/Else If/Else)

Use multiple condition steps for switch-like logic:

{
  "steps": [
    {
      "id": "check-tier-enterprise",
      "type": "condition",
      "config": {
        "conditions": {
          "left": "{{input.tier}}",
          "operator": "eq",
          "right": "enterprise"
        },
        "then": ["enterprise-flow"],
        "else": ["check-tier-pro"]
      }
    },
    {
      "id": "check-tier-pro",
      "type": "condition",
      "config": {
        "conditions": {
          "left": "{{input.tier}}",
          "operator": "eq",
          "right": "pro"
        },
        "then": ["pro-flow"],
        "else": ["free-flow"]
      }
    }
  ]
}

Guard Conditions

Exit early if a condition isn't met:

{
  "steps": [
    {
      "id": "validate-input",
      "type": "condition",
      "config": {
        "conditions": {
          "left": "{{input.required_field}}",
          "operator": "exists"
        },
        "then": ["continue-processing"],
        "else": []
      }
    }
  ]
}

An empty else: [] means the workflow ends without error if the condition is false.

Using Step Outputs

Reference previous step outputs in conditions:

{
  "steps": [
    {
      "id": "fetch-customer",
      "type": "action",
      "action": "stripe.getCustomer",
      "config": { "customerId": "{{input.customer_id}}" }
    },
    {
      "id": "check-balance",
      "type": "condition",
      "config": {
        "conditions": {
          "left": "{{steps.fetch-customer.output.balance}}",
          "operator": "gt",
          "right": "0"
        },
        "then": ["send-balance-reminder"],
        "else": ["send-thank-you"]
      }
    }
  ]
}

Common Patterns

Approval Routing

Route based on amount thresholds:

{
  "id": "route-approval",
  "type": "condition",
  "name": "Route by Amount",
  "config": {
    "conditions": {
      "operator": "or",
      "conditions": [
        {
          "left": "{{input.amount}}",
          "operator": "gt",
          "right": "50000"
        },
        {
          "left": "{{input.risk_score}}",
          "operator": "gt",
          "right": "80"
        }
      ]
    },
    "then": ["executive-approval"],
    "else": ["manager-approval"]
  }
}

Feature Flags

Check for optional features:

{
  "id": "check-feature",
  "type": "condition",
  "config": {
    "conditions": {
      "left": "{{input.features.advanced_analytics}}",
      "operator": "eq",
      "right": "true"
    },
    "then": ["run-advanced-analytics"],
    "else": ["run-basic-report"]
  }
}

Error Recovery

Check if a previous step succeeded:

{
  "id": "check-api-result",
  "type": "condition",
  "config": {
    "conditions": {
      "left": "{{steps.api-call.output.success}}",
      "operator": "eq",
      "right": "true"
    },
    "then": ["process-result"],
    "else": ["handle-error", "notify-admin"]
  }
}

Data Validation

Validate required fields before processing:

{
  "id": "validate-order",
  "type": "condition",
  "name": "Validate Order Data",
  "config": {
    "conditions": {
      "operator": "and",
      "conditions": [
        { "left": "{{input.customer_id}}", "operator": "exists" },
        { "left": "{{input.items}}", "operator": "exists" },
        { "left": "{{input.items.length}}", "operator": "gt", "right": "0" }
      ]
    },
    "then": ["process-order"],
    "else": ["reject-invalid-order"]
  }
}

Troubleshooting

Condition Always False

  1. Check template syntax - Ensure {{}} wrapping is correct
  2. Verify data types - Numbers should be compared as numbers
  3. Check for null values - Use exists operator first
  4. Debug with transform - Add a transform step to log values

Type Coercion

Values are compared as strings by default. For numeric comparisons, ensure both operands are numeric:

// May not work as expected (string comparison)
{
  "left": "{{input.count}}",
  "operator": "gt",
  "right": "9"  // "10" < "9" as strings!
}
 
// Works correctly (treated as numbers)
{
  "left": "{{input.count}}",
  "operator": "gt",
  "right": "9"  // Template engine handles numeric comparison
}

Missing Branches

If neither then nor else matches, the workflow continues to the next sequential step.

Next Steps