Search documentation

Search for pages in the documentation

If

Conditional branching based on expressions

The If node creates conditional branches in your workflow. Based on a CEL expression, data flows to either the "true" or "false" output, enabling different processing paths.

Overview

PropertyValue
CategoryControl
Node IDds.if.perItem.in1.success2.error0
Input Ports1
Success Outputs2 (true, false)
Error Outputs0
Execution ModePer-item

Configuration

ParameterTypeRequiredDescription
exprCEL ExpressionYesBoolean condition to evaluate

Expression Parameter

A CEL expression that evaluates to true or false:

cel
json.score > 0.8
cel
json.sentiment == "positive"
cel
json.attendees.size() > 3

How It Works

text
[If: expr?]
├─ True (Output 1) ──→ [Path A]
└─ False (Output 2) ─→ [Path B]
  1. Input data arrives at the If node
  2. The expr is evaluated with the input data
  3. If true: data flows to the first output (true branch)
  4. If false: data flows to the second output (false branch)
  5. Only ONE branch executes per item

Input Schema

Accepts any data from upstream. Access it via json in the expression.

Output Schema

Both outputs pass through the same input data unchanged:

json
// Input (same for both outputs)
{
  "meeting": { "title": "Q1 Review" },
  "score": 0.85,
  "sentiment": "positive"
}

The data is not modified—it's routed to the appropriate branch.

Examples

Basic Example: High Score Filter

Route based on a score threshold.

Configuration:

Expression: json.score > 0.8

Workflow:

text
    [AI: Score meeting]
            ↓
   [If: json.score > 0.8]
   ├─ True ──→ [Slack: "High priority!"]
   └─ False ─→ [Email: "Regular follow-up"]

Example: Check for Existence

Route based on whether data exists.

Expression: has(json.callRecording) && json.callRecording.transcript != ""

Workflow:

text
      [Load Meeting]
            ↓
   [If: has transcript?]
   ├─ True ──→ [AI: Analyze transcript]
   └─ False ─→ [Slack: "No transcript available"] → [Sink]

Example: String Comparison

Route based on classification result.

Expression: json.classification == "urgent"

Workflow:

text
       [AI: Classify urgency]
               ↓
[If: json.classification == "urgent"]
├─ True ──→ [SMS + Slack]
└─ False ─→ [Email only]

Example: Numeric Range

Handle multiple conditions with nested If nodes.

Workflow:

text
        [AI: Score deal]
              ↓
    [If: json.score >= 0.8]
    ├─ True ──→ [Handle High Score]
    └─ False ─→ [If: json.score >= 0.5]
                ├─ True ──→ [Handle Medium Score]
                └─ False ─→ [Handle Low Score]

Example: Array Size Check

Route based on number of items.

Expression: json.actionItems.size() > 0

Workflow:

text
       [AI: Extract action items]
                 ↓
 [If: json.actionItems.size() > 0]
 ├─ True ──→ [Create Tasks]
 └─ False ─→ [Sink]

Example: Complex Conditions

Combine multiple conditions.

Expression: json.sentiment == "negative" && json.score < 0.3

Workflow:

text
       [AI: Analyze meeting]
               ↓
   [If: negative AND low score?]
   ├─ True ──→ [Alert Manager] → [Create Risk Task]
   └─ False ─→ [Standard Processing]

CEL Expression Reference

Comparison Operators

OperatorMeaningExample
==Equalsjson.status == "active"
!=Not equalsjson.status != "closed"
>Greater thanjson.score > 0.5
>=Greater or equaljson.count >= 10
<Less thanjson.priority < 3
<=Less or equaljson.age <= 30

Logical Operators

OperatorMeaningExample
&&ANDjson.a > 1 && json.b < 5
||ORjson.x == 1 || json.y == 2
!NOT!json.processed

String Operations

ExpressionDescription
json.name.startsWith("A")Starts with
json.name.endsWith("z")Ends with
json.name.contains("abc")Contains substring
json.name.matches("^[A-Z].*")Regex match

Array/List Operations

ExpressionDescription
json.items.size()Array length
json.items.size() > 0Has items
"a" in json.tagsContains element

Null/Existence Checks

ExpressionDescription
has(json.field)Field exists
json.field != nullNot null
json.field ?? "default"Default if null

Best Practices

1. Always Connect Both Outputs

Every If node should have both branches connected:

Good:

text
[If]
├─ True ──→ [Action]
└─ False ─→ [Sink]

Bad:

text
[If]
├─ True ──→ [Action]
└─ False ─→ (disconnected)

2. Check for Existence Before Access

Prevent errors by checking if data exists:

Safe:

cel
has(json.callRecording) && json.callRecording.transcript != ""

Risky:

cel
json.callRecording.transcript != ""  // May error if callRecording is null

3. Use Clear Condition Names

Consider what each branch means:

text
[If: is_urgent?]   ← Clear meaning
├─ True ──→ [Urgent Handler]
└─ False ─→ [Normal Handler]

4. Keep Conditions Simple

For complex logic, compute upstream:

Better:

text
[AI: Classify] → [If: json.class == "A"]

Complex:

text
[If: (json.a > 5 && json.b < 3) || (json.c == "x" && json.d.size() > 2)]

5. Handle Edge Cases

Think about what happens for unexpected values:

cel
// What if score is null?
json.score > 0.8  // May error

// Safer:
has(json.score) && json.score > 0.8

Common Issues

Expression evaluation error

Symptom: Node fails with evaluation error

Cause: Accessing field that doesn't exist

Solution: Use has() to check existence:

cel
has(json.field) && json.field == "value"

Wrong branch executing

Symptom: Data goes to unexpected branch

Cause: Expression logic error

Solution:

  1. Check the expression carefully
  2. Log the input data to verify values
  3. Test with known inputs

Neither branch executing

Symptom: Workflow seems stuck

Cause: Expression throws error (execution fails)

Solution: Review expression for null access errors

  • Sink - Terminate false branches gracefully
  • AI Prompt - Generate classification for routing
  • Zip - Merge branches back together

Technical Details

Evaluation

The expression is evaluated using CEL (Common Expression Language):

  • Type-safe evaluation
  • Null-safe with has() checks
  • Supports complex expressions

Execution Mode

Per-item mode means:

  • Each input item is evaluated separately
  • Different items can route to different branches
  • 5 items might result in 3 true + 2 false

Output Port Order

  • Output 1 (index 0): True branch
  • Output 2 (index 1): False branch

The visual order in the UI should match: top/left = true, bottom/right = false.