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

# Templating & Expressions

> How Flow nodes reference upstream data and run parameters.

Flow nodes read two things at run time: **`inputs`** (a dict of upstream nodes' outputs, keyed by input port name) and **`parameters`** (the run's parameters, plus — inside a `for_each` body — the current item/index). Different node types expose two different ways to reference them in `config`.

## `llm_call` and `action_call` templating

`llm_call`'s `prompt`/`system_prompt` and `action_call`'s string `parameters` values are plain Python [`str.format()`](https://docs.python.org/3/library/string.html#format-string-syntax) templates, rendered against `inputs`/`parameters`:

```yaml theme={null}
- id: summarize
  type: llm_call
  config:
    prompt: "Summarize this ticket: {inputs[in][body]}"
```

```yaml theme={null}
- id: notify
  type: action_call
  config:
    action_id: "664f1c2e5a2ac9f0d39326"
    parameters:
      to: "{inputs[in][email]}"
      greeting: "Hello, {parameters[first_name]}!"
```

* Nested dict/list access uses `[key]` chains — no dots (e.g. `{inputs[in][user][email]}`, not `{inputs.in.user.email}`).
* `action_call` renders every **string** value in `parameters` this way, recursively through nested dicts/lists; non-string values (numbers, booleans) pass through unchanged.
* If a referenced key is missing, the node fails with a clear error naming the field and the missing key — e.g. `llm_call 'prompt' references 'foo', which is missing from inputs/parameters on this run` — rather than silently rendering an empty string.

## `if`, `for_each`, and `rest_call` query values: Python expressions

`if.config.left`, `for_each.config.items`, and any `rest_call.config.query` value that mentions `inputs` or `parameters` are evaluated as a **Python expression** (via `eval`), with `inputs` and `parameters` as the only names in scope:

```yaml theme={null}
- id: check_amount
  type: if
  config:
    left: "inputs['in']['amount'] > parameters['threshold']"
    operator: is_true
```

```yaml theme={null}
- id: process_items
  type: for_each
  config:
    items: "inputs['in']['orders']"
```

```yaml theme={null}
- id: search
  type: rest_call
  config:
    url: "https://api.example.com/search"
    query:
      q: "inputs['in']['query']"        # evaluated (mentions `inputs`)
      limit: "20"                        # sent as a literal string — no reference
```

<Warning>
  This is a genuine `eval()` against untrusted-shaped input — the subprocess sandbox each node runs in is the isolation boundary, not the expression syntax itself. Only reference `inputs`/`parameters`; there is no other trust model layered on top.
</Warning>

## `python` node: full function body

The `python` node doesn't template strings at all — it runs a complete Python module. Your `handler(inputs, parameters)` function receives both dicts directly and returns the node's output:

```yaml theme={null}
- id: transform
  type: python
  config:
    code: |
      def handler(inputs, parameters):
          order = inputs.get("in", {})
          return {"total": order["amount"] * 1.1}
```

## Passing data across a branch

When an `if` node's upstream input is a dict, its fields are merged through to the chosen branch alongside `branch` and `value`, so a node downstream of `true` (or `false`) still sees the original payload, not just the condition's result:

```json theme={null}
// if node output on the "true" branch, given upstream input {"ticket": {...}}
{ "ticket": { "...": "..." }, "branch": "true", "value": true }
```

## Reference

| Node type     | Field(s)                                  | Syntax                                                                                                 |
| ------------- | ----------------------------------------- | ------------------------------------------------------------------------------------------------------ |
| `llm_call`    | `prompt`, `system_prompt`                 | `str.format()` — `{inputs[...]}`, `{parameters[...]}`                                                  |
| `action_call` | `parameters` (string values, recursively) | `str.format()` — `{inputs[...]}`, `{parameters[...]}`                                                  |
| `if`          | `left`                                    | Python expression (`eval`) with `inputs`/`parameters` in scope                                         |
| `for_each`    | `items`                                   | Python expression (`eval`) with `inputs`/`parameters` in scope                                         |
| `rest_call`   | `query` values                            | Python expression (`eval`) when the string mentions `inputs`/`parameters`; otherwise sent as a literal |
| `python`      | `code`                                    | Full Python module; `handler(inputs, parameters)` receives both dicts as-is                            |

See also: [Node Reference](/reference/flows/nodes), [Overview](/reference/flows/overview).
