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

# Expressions

> Bind data between inputs, variables, and step outputs with the {{ }} expression language

Expressions let a flow reference live data at runtime. Anywhere you write `{{ ... }}` inside a [Flow Definition](/concepts/flow-definitions), the platform resolves the enclosed path against the execution's context — trigger input, definition variables, prior step outputs — just before the task runs.

```json theme={null}
{
  "parameters": {
    "message": "Hello {{input.name}}",
    "records": "{{load_step.output.rows}}"
  }
}
```

## Where expressions can appear

| Location               | How they're used                                                                                                                                                                                   |
| ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Task `parameters`      | Resolved right before the task executes. Values can be plain strings, whole-value expressions, or nested objects and arrays — expressions are resolved recursively at any depth.                   |
| Step `exit_conditions` | Each condition is a comparison (e.g. `{{step_1.output.status}} == "failed"`) evaluated after all tasks in the step complete, to decide whether the flow continues, skips the next step, or aborts. |

## Context roots

Every expression starts from one of these roots:

| Root      | Example                         | Resolves to                                                                                                                  |
| --------- | ------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| `input`   | `{{input.phone}}`               | The trigger's `input_data` — or, for multi-record jobs, this execution's record.                                             |
| `var`     | `{{var.threshold}}`             | A definition-level variable, by name.                                                                                        |
| `job`     | `{{job.id}}`                    | Details of the job this execution belongs to: `id`, `source`, and `metadata` (the metadata object from the trigger request). |
| `system`  | `{{system.execution_id}}`       | Runtime identifiers: `execution_id`, `definition_id`, and `definition_version`.                                              |
| A step ID | `{{step_1.task_a.output.cost}}` | The output of a completed task, addressed as `{{step_id.task_id.output.<field>}}`.                                           |

<Tip>
  When a step contains exactly one task, you can skip the task ID: `{{greet.output.message}}` is shorthand for `{{greet.echo_hello.output.message}}`.
</Tip>

## Dot-path traversal

Paths use dots to walk into nested objects: `{{input.contact.address.city}}`. Traversal is **null-safe** — if any segment along the path is missing, the expression resolves to `null` instead of raising an error. In a mixed string template, a `null` value renders as an empty string.

To supply a fallback, use the `default` pipe:

```text theme={null}
{{input.name | default:"Unknown"}}
{{step_1.task_a.output.cost | default: 0}}
```

The default value is coerced to its natural type: quoted strings stay strings, `true`/`false` become booleans, bare numbers become numbers, and `null` stays null. `default` is the only pipe operator available.

## Type preservation

How a value resolves depends on whether the expression stands alone:

* **Whole-value expression** — when the entire parameter value is a single `{{ ... }}`, the resolved value keeps its native type: an object stays an object, an array stays an array, a number stays a number. This is how you pass structured data (like a list of records) from one step to the next without serializing it.
* **Mixed template** — when an expression is embedded in surrounding text (`"Hello {{input.name}}"`), the result is rendered as a string.

## Conditions

Exit conditions compare two values with one of six operators:

`==`   `!=`   `>`   `<`   `>=`   `<=`

```text theme={null}
{{step_1.output.status}} == "failed"
{{validate.output.error_count}} > 0
```

Either side can be an expression or a literal — quoted strings, bare numbers, `true`/`false`, or `null`. A condition with no operator at all is evaluated for truthiness: it passes if the expression resolves to a truthy value.

<Note>
  The ordering operators (`>`, `<`, `>=`, `<=`) require both sides to be numeric; if either side can't be treated as a number, the condition evaluates to `false`. Equality (`==`, `!=`) works on any types.
</Note>

## Validation at creation time

When you create a definition, every string in the body is scanned for malformed expressions. If any are found, the request is rejected with `422` and a list of the problems:

```json theme={null}
{
  "detail": {
    "expression_errors": [
      "Unclosed expression in: 'Hello {{input.name'"
    ]
  }
}
```

Two classes of errors are caught at creation time:

* **Unclosed expressions** — a `{{` with no matching `}}`.
* **Unknown pipe operators** — anything other than `default` after a `|`.

<Warning>
  Path validity is not checked at creation time. An expression that references a field that never exists is accepted, and simply resolves to `null` at runtime — use the `default` pipe when a value might be absent.
</Warning>

## Next steps

<CardGroup cols={2}>
  <Card title="Flow definitions" icon="file-code" href="/concepts/flow-definitions">
    Where inputs, variables, steps, and tasks are declared.
  </Card>

  <Card title="Execution models" icon="clock" href="/concepts/execution-models">
    Immediate, scheduled, windowed, and signaled tasks.
  </Card>
</CardGroup>
