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

# Automation Conditions

> Add conditional logic to control automation flow

Conditions control whether work happens. They appear in two places in an automation, and both use the same expression language.

| Where                                  | What it decides                              |
| -------------------------------------- | -------------------------------------------- |
| On the **trigger**                     | Whether a matching event starts a run at all |
| On a **path leaving a condition step** | Whether the run continues down that branch   |

Under the hood a condition is a list of [expr](https://expr-lang.org) expressions joined by a logical operator. The builder writes those expressions for you from a field, an operator and a value, so you rarely type one by hand.

***

## Adding Conditions

### On the trigger

1. Select the trigger's event type
2. In the trigger configuration, add one or more conditions
3. Pick a field from the event, an operator, and a value
4. With more than one condition, choose **AND** or **OR**

### On a condition step

1. Add a **Condition** step to the canvas
2. Each branch leaving the step gets its own condition
3. Configure each branch's field, operator and value
4. Add the steps that should run on each branch

A condition step has no separate "else" field. A branch with no condition on it is the fall-through, and it is the way to express "otherwise".

***

## Comparison Operators

The builder offers these operators for scalar fields.

| Operator              | Expression | Example                              |
| --------------------- | ---------- | ------------------------------------ |
| equals                | `==`       | `status == "overdue"`                |
| not equals            | `!=`       | `status != "draft"`                  |
| greater than          | `>`        | `amount > 1000`                      |
| greater than or equal | `>=`       | `usagePercentage >= 0.8`             |
| less than             | `<`        | `daysUntilRenewal < 30`              |
| less than or equal    | `<=`       | `overdueDays <= 30`                  |
| in                    | `in`       | `status in ["issued", "overdue"]`    |
| not in                | `not in`   | `status not in ["void", "canceled"]` |
| is null               | `== nil`   | `invoiceId == nil`                   |
| is not null           | `!= nil`   | `invoiceId != nil`                   |

For array fields, the builder offers **contains**, **does not contain**, **is empty** and **is not empty**.

***

## Combining Conditions

### AND

Every condition must be satisfied. Select **AND** when you add more than one condition.

* Invoice status is `overdue` **AND** amount is greater than 1000

### OR

At least one condition must be satisfied. Select **OR**.

* Failure reason is `insufficient_funds` **OR** failure reason is `card_declined`

You can also write both into a single expression with `&&` and `||`:

```text theme={null}
stageEntered && stage == "Closed Won"
```

***

## Branching

### If / otherwise

Add a condition step with two branches: one carrying the condition, one left unconditioned as the fall-through.

**Example: only chase invoices that are still owed**

* Branch 1 — condition `{{get_invoice_step.outputs.amountDue}} > 0` → send an invoice reminder
* Branch 2 — no condition → the run ends here

### Several outcomes

Add more branches to the same condition step, one per outcome, each with its own condition, and leave one unconditioned as the catch-all.

**Example: reacting to a payment failure reason**

* `failureReason == "insufficient_funds"` → email asking the customer to top up
* `failureReason == "expired_card"` → send a billing info request
* no condition → notify the team on Slack

***

## Available Data

A condition can read two things: the triggering event's payload, and the outputs of steps that already ran.

### Trigger event data

Trigger conditions are evaluated directly against the event payload, so fields are referenced by their bare name — no braces.

```text theme={null}
status == "overdue"
usagePercentage >= 0.8
amount > 10000
```

The fields available depend on the event type. For example:

| Event type                                                | Fields you can filter on                                                                         |
| --------------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
| `payment_failed`                                          | `paymentId`, `accountId`, `invoiceId`, `paymentMethodId`, `amount`, `currency`, `failureReason`  |
| `invoice_status_updated`                                  | `invoiceId`, `accountId`, `status`, `externalReference`                                          |
| `subscription_status_updated`                             | `subscriptionId`, `status`                                                                       |
| `prepaid_usage_update`                                    | `accountId`, `priceId`, `productId`, `currentUsage`, `maxUsageBeforeOverages`, `usagePercentage` |
| `subscription_renewal_window_entered`                     | `subscriptionID`, `accountID`, `termEnd`, `daysUntilRenewal`, `currency`, `currentACV`           |
| `salesforce_opportunity_changed` / `hubspot_deal_changed` | `provider`, `integrationID`, `recordID`, `stage`, `stageEntered`, `changedFields`                |

Every event type's full payload is in [the reference](/docs/automations/reference#event-payloads).

### Step outputs

Conditions on a path reference earlier data with a `{{...}}` path:

```text theme={null}
{{get_invoice_step.outputs.amountDue}} > 0
{{retry_step.outputs.status}} == "failed"
{{get_crm_step.outputs.stage}} == "Closed Won"
```

Lookup actions return the reduced invoice and subscription shapes — `status`, `amountDue`, `overdueDays`, `dueInDays`, `autoRenew`, `daysUntilRenewal`, `acv`, `arr`, `mrr` and more. The CRM, quote and bulk actions return their own result fields, including a `reason` explaining why an action wrote nothing.

<Note>
  Conditions read what the run actually carries. There is no derived customer segment, lifetime value, or other computed attribute available to an automation condition — filter on the concrete fields above, or on tags via a preceding lookup step.
</Note>

***

## Collections and Null

Because conditions are expr expressions, collection helpers are available:

```text theme={null}
len(linkedQuoteIds) > 0
any(invoices, .status == "overdue")
all(subscriptions, .autoRenew)
```

Null handling matters most on CRM change events, which carry only the fields that changed. An identifier that is absent resolves to nil rather than breaking the expression, so `changedFields.Amount.after` is safe to write. An expression that cannot be evaluated against the payload is treated as not satisfied — it will not accidentally let a run through.

***

## Common Patterns

### Only act on invoices still owed

**Steps:** `get_invoice` → condition

**Condition:** `{{get_invoice_step.outputs.amountDue}} > 0`

Prevents a reminder going out to a customer who paid while the automation was waiting.

### High-value failed payment alert

**Trigger:** `payment_failed`

**Trigger condition:** `amount > 10000`

**Then:** `send_slack_message` to your revenue channel.

### Only failures tied to an invoice

**Trigger:** `payment_failed`

**Trigger condition:** `invoiceId != nil`

`retry_invoice_payment` needs an invoice, so this keeps the run from starting for a payment that has none.

### Retry only when the last attempt failed

**Steps:** `retry_invoice_payment` → condition

**Condition:** `{{retry_step.outputs.status}} == "failed"`

### Act only when a deal enters a stage

**Trigger:** `salesforce_opportunity_changed` or `hubspot_deal_changed`

**Trigger condition:** `stageEntered && stage == "Closed Won"`

`stageEntered` is false the first time a record is seen, so this fires on the actual transition rather than on every sync.

***

## Nested Logic

Conditions do not nest inside one another. Build layered logic by chaining condition steps: a branch of one condition step can lead into another condition step, and so on.

**Example:**

1. Condition step — is this a CRM renewal opportunity?
   * Yes → condition step: is the subscription still auto-renewing?
     * Yes → `cancel_renewal_for_lost_opportunity`
     * fall-through → end
   * fall-through → end

***

## Best Practices

<CardGroup cols={2}>
  <Card title="Filter at the Trigger" icon="filter">
    A trigger condition is cheaper than starting a run and stopping it in step two.
  </Card>

  <Card title="Re-check Before Acting" icon="rotate">
    After a delay, look the record up again and condition on its current state.
  </Card>

  <Card title="Always Add a Fall-Through" icon="code-branch">
    Leave one branch unconditioned so a run has somewhere to go.
  </Card>

  <Card title="Test Edge Cases" icon="flask">
    Test with boundary values, and with events that are missing optional fields.
  </Card>
</CardGroup>

***

## Next Steps

<CardGroup cols={2}>
  <Card title="Actions" icon="play" href="/docs/automations/actions">
    Explore available actions.
  </Card>

  <Card title="Triggers" icon="bolt" href="/docs/automations/triggers">
    Configure what starts your automations.
  </Card>

  <Card title="Reference" icon="book" href="/docs/automations/reference">
    Condition syntax and full event payloads.
  </Card>
</CardGroup>
