# If / else

Route execution down different branches based on conditions over the previous node's output.

## Overview

The If/else node evaluates an ordered list of conditions against the upstream payload. The first condition that matches takes its branch; if none match, execution falls through the **Else** branch. Each condition exposes its own labeled output handle, so you wire downstream nodes directly to a specific case.

Conditions can be written as:

- **Expressions** — Common Expression Language style: `input.event_type == "checkout"`, `input.score > 0.8`, `input.tags contains "vip"`.
- **Builder rows** — pick a JSON path on the upstream payload, an operator, and a comparison value.

The upstream payload is normally the parsed output of the previous node (`input.foo.bar`). Plain literals like `42`, `"sales"`, `true`, or `null` work as right-hand operands.

## Contextual autocomplete

The expression field suggests as you type, in **three stages** that follow your position in the expression:

1. **Field** — upstream fields from the previous node's output schema (`input.output_text`, `input.output_parsed.<field>`), each tagged with its type (STRING, NUMBER, BOOLEAN, ENUM…).
2. **Operator** — filtered by the selected field's type: numbers get `==`, `!=`, `>`, `>=`, `<`, `<=`; booleans `==`, `!=`; enums `==`, `!=`, `in`; strings `==`, `!=`, `contains`, `in`.
3. **Value** — literals from the schema: enum values quoted and ready to insert, `true`/`false` for booleans.

The field stage also offers **ready-made expressions**: for a boolean field you can pick `flag == true` in one go, and for an enum field one complete `field == 'value'` per possible value — no need to remember the schema.

## Configuration

| Field | Description |
| --- | --- |
| `conditions` | Ordered list of cases. Each has a `caseName` (label shown on the handle), an `expression` (or `path`+`operator`+`value`), and a generated `handle` id (`true`, `branch-1`, `branch-2`, …). |
| `conditionMode` | `expression` (free-form CEL-like string) or `builder` (path + operator + value). |
| `conditionJoin` | `all` (AND) or `any` (OR) — applies when the builder mode has multiple rows. |
| Operators | `==`, `!=`, `>`, `>=`, `<`, `<=`, `contains`, `in`, `exists`, `empty`, `not_empty`. |
| Output handles | `true` (first match), `branch-1`, `branch-2`, … (subsequent matches), `false` (else). |

When no structured condition is configured, a plain-text condition is passed to the LLM, which answers `true`/`false`. Use this for fuzzy intent checks (`"user is asking about pricing"`).

## Branch on the trigger event

If/else can read the inbound trigger payload directly — no Set state bridge needed. The `event.*` operands (and the alias `webhook.*`, pointing at the same payload) expose `{ body, headers, query, method, path }`:

| Expression | Branches on |
| --- | --- |
| `event.method == 'POST'` | The HTTP verb |
| `event.body.action == 'deleted'` | A field of the request body |
| `event.query.format == 'pdf'` | A query-string param |
| `event.headers.x-source contains 'github'` | An incoming header |

`event` is the trigger-agnostic alias; `webhook` resolves to the same payload. Both work in expression mode and in the builder (path + operator + value). The autocomplete surfaces a **Trigger event** group (method / path / query / headers / body) plus ready-made `event.method == 'VERB'` picks for the verbs the webhook accepts — so you can split a single endpoint by HTTP method in one click.

```
Webhook → If/else
   ├─ event.method == 'POST'   → Create record (Agent)
   ├─ event.method == 'DELETE' → Soft-delete (Agent)
   └─ else                     → 405 (Respond to webhook)
```

## Example

Route an inbound webhook by event type:

```
Webhook → If/else
   ├─ event == "checkout.completed"  → Send invoice (Agent)
   ├─ event == "subscription.canceled" → Notify ops (Slack MCP)
   └─ else                              → Log & ignore (Transform)
```

Condition rows:

| Case name | Expression |
| --- | --- |
| Checkout | `input.type == "checkout.completed"` |
| Canceled | `input.type == "subscription.canceled"` |

The first matching row fires. Everything else exits through **Else**.

## Gotchas

- Condition order matters. The node short-circuits on the first match.
- `input` always resolves to the parsed JSON of the upstream node (`last_output_parsed`). If the upstream node returned plain text, use `input.output_text` instead.
- An unconnected branch is a no-op — execution stops cleanly on that handle.
- The LLM fallback costs a model call. Prefer structured conditions for hot paths.
- For numeric comparisons, the operand is coerced to `float`. Comparing a non-numeric string returns `false`.

---

*Source: https://agentbuilder.systalink.sn/docs/node-if-else — human documentation.*
*Other language: [/docs-md/fr/node-if-else.md](/docs-md/fr/node-if-else.md).*
*Machine-readable index: [/llms.txt](/llms.txt).*
