> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ingestly.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Conditional routing

> Branch a pipeline down different paths based on rules over upstream data.

Ingestly lets you branch a pipeline by setting **conditions on edges**. Every edge between two nodes has a condition that decides whether the edge fires when the source completes. By configuring those conditions, you can send a document down different paths based on its content, the previous step's status, or a custom rule.

## Edge condition types

Click any edge in the [pipeline editor](/pipelines/editor) to open the **properties panel** on the right. The **Condition Type** selector controls when the edge fires:

| Type           | Fires when                                                                         |
| -------------- | ---------------------------------------------------------------------------------- |
| **Always**     | The source node completed (success or failure). Use this for unconditional flow.   |
| **On success** | The source completed successfully. This is the default for new edges.              |
| **On failure** | The source failed. Pair with **On success** to route errors to a separate handler. |
| **Condition**  | A rule over the source node's payload evaluates to true.                           |
| **Otherwise**  | None of the source's other **Condition** edges fired. Use this as a fallback.      |

## Authoring a Condition edge

When you pick **Condition** as the edge type, the properties panel shows a structured editor with three fields:

* **Field:** the value to compare. Type `{{` to autocomplete fields from the source node (for example, `{{payload.invoiceType}}`).
* **Operator:** the comparison to apply. The dropdown filters operators by the inferred field type, so numeric operators stay hidden when the field is a string.
* **Value:** the literal to compare against. Hidden for unary operators like **is empty** that don't take a value.

Toggle **Edit raw expression** to edit the condition as a single expression string instead. This is useful for power users who want to copy and paste expressions or use less common syntax. Toggling back into the structured editor works as long as the raw expression uses the canonical operator set.

### Operators

The structured editor surfaces these operators:

| Operator       | Aliases (raw expression) | Notes                                                              |
| -------------- | ------------------------ | ------------------------------------------------------------------ |
| `==`           | `eq`, `equals`           | Case-insensitive string compare.                                   |
| `!=`           | `neq`, `not_equals`      | Case-insensitive string compare.                                   |
| `>`            | `gt`                     | Numeric when both sides parse as numbers, lexicographic otherwise. |
| `<`            | `lt`                     | Same as above.                                                     |
| `>=`           | `gte`                    | Same as above.                                                     |
| `<=`           | `lte`                    | Same as above.                                                     |
| `contains`     | (none)                   | Case-insensitive substring match.                                  |
| `not_contains` | `notcontains`            | Inverse of `contains`.                                             |
| `is_empty`     | `isempty`                | Unary. True when the field is missing, null, or an empty string.   |
| `is_not_empty` | `isnotempty`             | Unary. Inverse of `is_empty`.                                      |

Aliases work in raw-expression mode but always serialize to the canonical form when saved through the structured editor.

## Condition groups

A single rule is often not enough. A **Condition** edge can hold a nested group of rules combined with **AND** or **OR**, so you can express logic like "amount over 1000 **and** (region is EU **or** flagged is true)".

In the structured editor, a group has:

* **Match all / Match any:** the toggle at the top of a group. **All** requires every child to match (AND); **Any** requires at least one (OR).
* **Add condition:** append a leaf rule (a field, operator, and value) to the group.
* **Add group:** append a nested group, so you can mix AND and OR at different levels.
* **NOT:** negate a group so it matches when the group is **not** satisfied.
* **Duplicate** and **Delete:** on each condition and nested group.

An empty group prompts you to "Add a condition or a group below." Groups can nest several levels deep.

### Referencing another node's output

A condition is not limited to the source node. Type `{{` in a **Field** or **Value** box to autocomplete fields from any upstream node, and the editor inserts a `{{nodeName.field}}` token. This lets a condition compare a value from one node against a value from another, for example `{{extract.payload.total}} != {{reconcile.payload.expectedTotal}}`.

## Set a condition after creating an edge

When you connect two nodes by dragging an edge, Ingestly creates the edge with the default **On success** condition. To change it, click the edge to open the **properties panel** on the right, then pick a new **Condition Type**. See [Edge condition types](#edge-condition-types) for what each type does.

## Branch from a node

To author multiple outgoing branches at once, click the **branch** action in any node's hover toolbar. The **Add branches** modal opens with two rule rows by default:

* Each row is a structured field, operator, and value.
* Click **Add branch** to add more rows.
* Tick **Add an Otherwise edge for the fallback case** to also create an Otherwise edge for inputs that match no rule.

When you click **Create**, Ingestly creates one outgoing edge per row plus the optional Otherwise edge. Each new edge is wired to a placeholder target that you can replace by clicking it and picking a real node from the search list.

<Tip>Branches evaluate independently. If two rules can be true at the same time, both edges fire and the downstream paths run in parallel. Make rules mutually exclusive when you want exactly one path to fire.</Tip>

## Otherwise edges

An **Otherwise** edge fires only when none of the source node's other **Condition** edges matched. The scheduler resolves Otherwise edges after evaluating siblings, so you don't have to write a negation for every condition.

A few rules:

* A node can have at most one Otherwise edge. Saving a pipeline with two Otherwise edges from the same source raises a validation error.
* Otherwise edges look at sibling **Condition** edges only. **On success**, **On failure**, and **Always** edges are independent and don't suppress an Otherwise.
* An Otherwise edge does not fire when the source failed. Use **On failure** for the failure path.

### Example: route by document type

```
Parse Action
   ├── Condition: {{payload.invoiceType}} == 'B2B' → Extract (B2B schema)
   ├── Condition: {{payload.invoiceType}} == 'B2C' → Extract (B2C schema)
   └── Otherwise → Review queue
```

If the parsed type is `B2B` or `B2C`, only that branch fires. Anything else falls into the review queue.

## Edge conditions vs the Split action

The [Split action](/nodes/split) node and edge conditions both fan a run out into multiple paths but they answer different questions:

* **Edge conditions** decide whether a single edge fires. Use them when you want a few branches with explicit rules and an optional fallback.
* **Split action** cuts one document into contiguous page groups and starts a child run for each (for example, one upload holding five invoices). Use it when a single file really holds several documents.
* **Filter action** keeps or drops pages within one document. It narrows a run, it never fans one out.

For most "if document is X, do Y" scenarios, edge conditions are the right tool.

## Tips

* Configure an **Otherwise** edge whenever you have a set of mutually exclusive conditions. Without one, runs that match no condition stop at the source node.
* Keep conditions simple. If you need complex boolean logic, chain conditions through intermediate transform or validation nodes that produce a flag in the payload.
* Test each branch with a representative document before activating the pipeline.
