# Condition

Split the run into up to twelve lanes plus a None lane, checked top to bottom — the first lane whose conditions all pass wins, and Condition tests one field against one value at a time.

## Getting here

**Automate → Workflows**, open a workflow, click **+** on the canvas, then pick **Condition** under **Add Steps**.

## Before you start

- An **owner**, **admin** or **agent** can add this step and save the workflow. Publishing it is an **owner** or **admin** action.
- Building and running workflows depends on your plan; see [pricing](/pricing).
- Nothing outside Inchat has to be connected. A condition only reads fields the run already has — from the trigger, an earlier step's answer, or an HTTP Request's saved response.

## Set up

1. Open the workflow and click **+** where the split belongs.
2. Pick **Condition**.
3. Name the first lane (optional — it defaults to **Segment 1**) and add one or more conditions to it. With more than one condition on a lane, choose **all match (AND)** or **any matches (OR)**.
4. Click **+ Add segment** for each further lane. Lanes are checked in order, so put the narrowest one first.
5. Leave the **None** lane's steps for contacts that match nothing — it always exists and cannot be removed.
6. Save the step, then publish the workflow.

## Fields

| Field | What it means | Default | Limits |
| --- | --- | --- | --- |
| Segment | One lane: a label, up to ten conditions, and its own steps. The first lane (top to bottom) whose conditions all pass is the one that runs. | — | Up to 12 lanes, each with up to 10 conditions — saving refuses more than either, even though the drawer itself does not stop you from adding them. The last remaining segment cannot be deleted — a Condition step always keeps at least one. |
| all match (AND) / any matches (OR) | How a lane's own conditions combine when it has more than one. Shown only once a lane has two or more conditions. | all match (AND) | — |
| None lane | Runs when no lane's conditions matched. Always present, and not itself a segment. | — | — |

## How it works

- Lanes are checked top to bottom; the **first** lane whose conditions all pass (or, for **any matches**, whose conditions has one pass) is the one that runs, and every lane after it is skipped even if it would also have matched.
- A condition compares one field to one typed value with an operator — equals, contains, exists, greater/less than, between, or a tag-list check such as has any/has all/has none — and a value is trimmed and case-folded before comparing, so `VIP` and `vip ` are the same tag.
- Almost every field is the value the run captured when its trigger fired, or that a later step wrote into it — it does not re-read the database at the moment the Condition step runs. **contact.marketing_consent** is the one exception: it is re-read from the contact's current record immediately before the check, so a customer who changes their answer during a wait is honored rather than judged on a stale snapshot.
- If a field the condition names is missing from the run (the trigger that started this run never set it), the field reads as not present: **eq**/**contains** do not match, **exists** is false, and **not exists** is true.
- None of this needs anything else running: a Condition step with genuinely no matching lane simply takes **None** and carries on.
- The same underlying logic also runs as a plain two-way **branch** action (all conditions must pass, Then or Else) — an older step type with no entry of its own in the step menu. It still exists in the engine and can appear on a canvas the AI builder wrote, or one built before Condition existed; there is no way to add a fresh one from the menu, where **Condition** is what an operator always gets.

## When it fails

- A Condition step does not fail at runtime in the way a send or a lookup can — it always resolves to some lane, even when that lane is None, so there is no error state to read in the **Activity** tab for this step by itself.
- If a condition's own check throws unexpectedly (a malformed stored value, for instance), that one lane is treated as not matching rather than stopping the run — evaluation continues to the remaining lanes, so the step still resolves to whichever of them matches, or to None if none do.

## Limits

- A field backed by a database write — a tag, a saved contact field, a lifecycle stage — is NOT refreshed mid-run once the run has its own copy of it, with **contact.marketing_consent** as the one exception that is re-read live. So a tag added by an earlier step in the SAME run is not visible to a later Condition step, because tagging writes straight to the contact's record. A field a step writes directly into the run itself — an HTTP Request's saved response, a Randomizer's **split.lane**, an answered question — has no such lag: it is visible to a later Condition in the same run the moment it is written, since those steps write into the run's own working set rather than the database.
- Saving refuses more than 12 lanes, or more than 10 conditions on one lane — the drawer's own segment editor does not stop you from adding more than either while you build, only the save boundary does.
- A Condition step only chooses a lane — it sends nothing to the customer and changes nothing about the conversation by itself. Whatever effect a lane has comes from the steps placed inside it.
- Reordering or deleting a lane changes which contacts take it on every future run, but does not touch anything about a run already in progress or already finished.

## Best practices

- Put the narrowest, most specific lane first — the first match wins, so a broad lane placed early can silently swallow contacts meant for a more specific one further down.
- Give every lane a real label once there is more than one — **Segment 2** tells nobody, months later, what that lane was for.
- Leave the None lane populated with something, even a single tag, so a contact matching nothing is still visible somewhere later rather than quietly falling off the workflow.

## Use cases

- A lane for **Contact lifecycle stage** equals **Hot Lead** routes to a priority path; every other lifecycle stage falls to None and gets a standard reply.
- Two lanes read **contact.marketing_consent** — one for contacts who agreed, one (via **any matches**) for everyone else — so a chase respects a withdrawal that happened mid-wait.
- A lane checks an HTTP Request step's saved status field to branch on whether a courier lookup succeeded, with a fallback lane for a failed or empty response.

## FAQ and troubleshooting

### What happens if two lanes could both match the same contact?

Only the first one, in the order they are listed. Reorder the lanes to change which one wins.

### Does a field I never set for this trigger just fail the condition?

It reads as not present — an “equals” or “contains” condition on it does not match, and an “exists” condition on it is false.

### I see a “branch” step type mentioned in places — is that the same as Condition?

It is the same all-conditions/then-else logic with only two lanes and no menu entry of its own. You cannot add a fresh one; **Condition** is what the step menu always gives you.

## Related

- [Randomizer (A/B split)](https://inchat.inseller.my/help/workflows/randomizer-ab-split)
- [Conditions and variables](https://inchat.inseller.my/help/workflows/conditions-and-variables)
- [Jump To](https://inchat.inseller.my/help/workflows/jump-to)
- [Date & Time (business hours)](https://inchat.inseller.my/help/workflows/date-and-time-business-hours)
- [Workflows: how a flow runs](https://inchat.inseller.my/help/workflows/workflows-overview)

---

Source files: `src/lib/workflows/ai-builder/spec.ts`, `src/lib/workflows/canvas-model.ts`, `src/components/workflows/WorkflowStepConfigDrawer.tsx`, `src/lib/workflows/engine.ts`, `src/lib/workflows/engine-condition.ts`, `src/lib/workflows/condition-fields.ts`, `src/lib/workflows/live-consent-field.ts`, `src/lib/workflows/engine.test.ts`
