> ## 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.

# Node Reference

> Detailed reference for every node type available in Datazone Flows

## Common Properties

Every node in a flow document shares the same envelope:

| Property | Type | Description |
| - | - | - |
| `id` | string | Unique identifier for the node within the flow |
| `type` | string | Node type: `start`, `if`, `for_each`, `llm_call`, `rest_call`, `action_call`, `python`, `sql`, `response`, or `output` |
| `label` | string | (Optional) Display name shown on the canvas |
| `config` | object | Type-specific configuration, validated against that node type's schema |

<Note>
  Each node type also declares a fixed set of **input** and **output** ports (e.g. `in` / `out`). [Connections](/reference/flows/yaml-reference#connections) wire an upstream node's output port to a downstream node's input port.
</Note>

## Start

The entry point of a flow. It takes no configuration and no inputs — it simply emits the run's parameters and a trigger timestamp so downstream nodes have a well-defined starting payload.

| Property | Category | Inputs | Outputs |
| - | - | - | - |
| `start` | trigger | — | `out` |

```yaml theme={null}
- id: trigger
  type: start
  label: Start
  config: {}
```

**Output:**

```json theme={null}
{
  "kind": "start",
  "parameters": { "...": "the run's parameters" },
  "triggered_at": "2026-07-24T12:00:00+00:00"
}
```

<Note>
  Every flow must have exactly one `start` node as its entry point.
</Note>

## If

Branches the flow based on a condition evaluated against upstream data and flow parameters.

| Property | Category | Inputs | Outputs |
| - | - | - | - |
| `if` | control (**branching**) | `in` | `true`, `false` |

| Attribute | Type | Default | Description |
| - | - | - | - |
| `left` | string | — | Python expression evaluated with `inputs`/`parameters` in scope (required) |
| `operator` | string | `eq` | One of `eq`, `neq`, `gt`, `gte`, `lt`, `lte`, `contains`, `is_true`, `is_false`, `is_empty`, `is_not_empty` |
| `right` | any | `null` | Right-hand operand. Ignored for `is_true`/`is_false`/`is_empty`/`is_not_empty` |

```yaml theme={null}
- id: check_status
  type: if
  label: Is approved?
  config:
    left: "inputs['in']['status']"
    operator: eq
    right: "approved"
```

Because `if` is a **branching** node, every outgoing connection from it must set `from_port` to `true` or `false`:

```yaml theme={null}
connections:
  - from: check_status
    to: send_email
    from_port: "true"
  - from: check_status
    to: log_rejection
    from_port: "false"
```

<Warning>
  A connection leaving a branching node without a valid `from_port` fails validation. Nodes only reachable through the branch that isn't chosen at run time are marked `SKIPPED` instead of executed.
</Warning>

When the upstream input is a dict, its fields are merged into the output alongside `branch` and `value`, so downstream nodes on either branch still see the original payload.

## For Each

Iterates over a list, re-running a nested **body** sub-flow once per item.

| Property | Category | Inputs | Outputs |
| - | - | - | - |
| `for_each` | control (**container**) | `in` | `out` |

| Attribute | Type | Default | Description |
| - | - | - | - |
| `items` | string | — | Python expression evaluated with `inputs`/`parameters` in scope; must evaluate to a list/iterable (required) |
| `item_as` | string | `item` | Parameter name the current item is exposed under inside the body, as `parameters[item_as]` |
| `index_as` | string | `index` | Parameter name the current index is exposed under inside the body (omit to disable) |
| `max_iterations` | int | `1000` | Safety cap — exceeding it raises a run error |
| `save_as` | string | `results` | Output key the collected per-iteration results are stored under |
| `body` | object | — | A full nested flow document (its own `nodes`/`connections`), required |

```yaml theme={null}
- id: process_orders
  type: for_each
  label: Process each order
  config:
    items: "inputs['in']['orders']"
    item_as: order
    index_as: index
    max_iterations: 500
    save_as: results
    body:
      flow:
        name: process_orders_body
      nodes:
        - id: body_start
          type: start
          config: {}
        - id: format_order
          type: python
          config:
            code: |
              def handler(inputs, parameters):
                  order = parameters["order"]
                  return {"id": order["id"], "total": order["amount"] * 1.1}
        - id: body_response
          type: response
          config: {}
      connections:
        - from: body_start
          to: format_order
        - from: format_order
          to: body_response
```

<Note>
  The `body` document is validated the exact same way a top-level flow is, including supporting another `for_each` nested inside it (up to a max depth of 5). It **must contain exactly one `response` node** — that node's output is what gets collected into `save_as` for each iteration.
</Note>

## LLM Call

Calls a Large Language Model through a configured [Model Account](/reference/development/model-accounts), or the organization's Orion AI default account when omitted.

| Property | Category | Inputs | Outputs |
| - | - | - | - |
| `llm_call` | ai (needs credentials) | `in` | `out` |

| Attribute | Type | Default | Description |
| - | - | - | - |
| `model_account_id` | string | `null` | Model account to use. Omit to use the organization's Orion AI settings |
| `model` | string | `null` | Model enum value (e.g. `GPT_4O`). Omit to use the organization default |
| `prompt` | string | — | Prompt template, required. Supports `{inputs[...]}` / `{parameters[...]}` substitution |
| `system_prompt` | string | `null` | (Optional) System prompt, same templating support |
| `save_as` | string | `response` | Output key the model's response text is stored under |
| `timeout_sec` | int | `60` | Request timeout in seconds |

```yaml theme={null}
- id: summarize
  type: llm_call
  label: Summarize ticket
  config:
    model_account_id: "664f1c2e5a2ac9f0d39326"
    model: GPT_4O
    system_prompt: "You are a support triage assistant."
    prompt: "Summarize this ticket in one sentence: {inputs[in][body]}"
    save_as: summary
    timeout_sec: 45
```

<Note>
  `prompt`/`system_prompt` use Python's `str.format()` templating (not Jinja) — see [Templating & Expressions](/reference/flows/templating#llm_call-and-action_call-templating) for the exact rules and error behavior when a referenced field is missing.
</Note>

## REST Call

Calls an external HTTP/REST endpoint.

| Property | Category | Inputs | Outputs |
| - | - | - | - |
| `rest_call` | integration | `in` | `response` |

| Attribute | Type | Default | Description |
| - | - | - | - |
| `url` | string | — | Target URL, required. Must start with `http://` or `https://` |
| `method` | string | `GET` | One of `GET`, `POST`, `PUT`, `PATCH`, `DELETE` |
| `headers` | object | `{}` | Request headers |
| `query` | object | `{}` | Query parameters. Values that reference `inputs`/`parameters` are evaluated as Python expressions; plain literals are sent as-is |
| `json_body` | object | `null` | JSON request body |
| `timeout_sec` | int | `30` | Request timeout in seconds |
| `save_as` | string | `response` | Output key the parsed response body is stored under |
| `auth_type` | string | `none` | One of `none`, `basic`, `bearer`, `api_key`, `oauth2` |
| `username` / `password` | string | `null` | Used when `auth_type: basic` |
| `token` | string | `null` | Used when `auth_type: bearer` — sent as `Authorization: Bearer <token>` |
| `api_key_name` / `api_key_value` | string | `null` | Used when `auth_type: api_key` — sent as a `<api_key_name>: <api_key_value>` header |
| `token_url` | string | `null` | Used when `auth_type: oauth2` — the OAuth2 token endpoint. Required |
| `client_id` / `client_secret` | string | `null` | Used when `auth_type: oauth2` — client credentials sent to `token_url`. Required |
| `scope` | string | `null` | Used when `auth_type: oauth2` — optional scope requested with the token |

```yaml theme={null}
- id: fetch_weather
  type: rest_call
  label: Fetch London weather
  config:
    url: "https://api.open-meteo.com/v1/forecast"
    method: GET
    query:
      latitude: "51.5074"
      longitude: "-0.1278"
      current_weather: "true"
    timeout_sec: 30
    save_as: weather
```

Authenticated example:

```yaml theme={null}
- id: call_billing_api
  type: rest_call
  label: Get invoice
  config:
    url: "https://billing.example.com/api/invoices/{parameters[invoice_id]}"
    method: GET
    auth_type: bearer
    token: "sk_live_..."
    save_as: invoice
```

OAuth 2.0 (Client Credentials) example:

```yaml theme={null}
- id: call_partner_api
  type: rest_call
  label: List orders
  config:
    url: "https://api.partner.example.com/v1/orders"
    method: GET
    auth_type: oauth2
    token_url: "https://auth.partner.example.com/oauth/token"
    client_id: "datazone-client"
    client_secret: "..."
    scope: "orders.read"
    save_as: orders
```

With `oauth2`, the node first sends a `client_credentials` grant to `token_url`, then calls `url` with the returned `access_token` as an `Authorization: Bearer` header. A new token is fetched every time the node runs; tokens are not cached between runs. In the Flow Builder, choose **OAuth 2.0 (Client Credentials)** in the node's **Authentication** field, or start from the **REST OAuth Template** in the template gallery.

The response is always returned alongside its `status_code`:

```json theme={null}
{ "response": { "...": "parsed JSON or raw text" }, "status_code": 200 }
```

## Action Call

Calls a project [Action](/reference/development/actions) — a user-defined Python function — with parameters.

| Property | Category | Inputs | Outputs |
| - | - | - | - |
| `action_call` | integration (needs credentials) | `in` | `out` |

| Attribute | Type | Default | Description |
| - | - | - | - |
| `action_id` | string | — | Id of the Action to call, required |
| `parameters` | object | `{}` | Parameters passed to the action. String values support `{inputs[...]}`/`{parameters[...]}` templating |
| `save_as` | string | `result` | Output key the action's return value is stored under |
| `timeout_sec` | int | `30` | Timeout in seconds |

```yaml theme={null}
- id: send_welcome_email
  type: action_call
  label: Send welcome email
  config:
    action_id: "664f1c2e5a2ac9f0d39326"
    parameters:
      to: "{inputs[in][email]}"
      name: "{inputs[in][full_name]}"
    save_as: email_result
    timeout_sec: 30
```

<Note>
  Actions are audited the same way as when an agent calls them — every `action_call` node execution appears in the Action's call history.
</Note>

## Python

Runs custom Python against upstream data and flow parameters, in an isolated subprocess.

| Property | Category | Inputs | Outputs |
| - | - | - | - |
| `python` | transform | `in` | `out` |

| Attribute | Type | Description |
| - | - | - |
| `code` | string | A complete Python module defining a `handler(inputs, parameters)` function (required) |

```yaml theme={null}
- id: convert_temperature
  type: python
  label: Convert temperature
  config:
    code: |
      def handler(inputs, parameters):
          # inputs     – dict of upstream node outputs, keyed by port name
          # parameters – dict of flow run parameters
          weather = inputs.get("in", {}).get("weather", {})
          celsius = weather.get("current_weather", {}).get("temperature")
          fahrenheit = round(celsius * 9 / 5 + 32, 1)
          return {"celsius": celsius, "fahrenheit": fahrenheit}
```

<Warning>
  `code` must define a `handler(inputs, parameters)` function and be valid Python — both are checked before the flow can run.
</Warning>

## SQL

Runs a read-only SQL `SELECT` against the project's own datasets, the same way SQL Explorer does, and returns the rows to the flow.

| Property | Category | Inputs | Outputs |
| - | - | - | - |
| `sql` | data (needs credentials) | `in` | `out` |

| Attribute | Type | Default | Description |
| - | - | - | - |
| `query` | string | — | The `SELECT` query, required. Supports `str.format()` templating — see [Templating & Expressions](/reference/flows/templating#llm_call-action_call-and-sql-templating) |
| `save_as` | string | `result` | Output key the query result is stored under |
| `timeout_sec` | int | `60` | Request timeout in seconds |

```yaml theme={null}
- id: get_orders
  type: sql
  label: Fetch recent orders
  config:
    query: "SELECT * FROM orders WHERE id = {parameters[order_id]} LIMIT 100"
    save_as: result
    timeout_sec: 60
```

**Output:**

```json theme={null}
{
  "result": {
    "data": [{ "id": 1, "amount": 10 }, { "id": 2, "amount": 20 }],
    "columns": ["id", "amount"],
    "row_count": 2
  }
}
```

<Note>
  `query` uses the same `str.format()` templating as `llm_call`/`action_call` — a field referenced in `query` but missing from `inputs`/`parameters` fails the node with a clear error naming the missing key, rather than sending a malformed query.
</Note>

<Warning>
  Only `SELECT` (and `UNION`/`INTERSECT`/`EXCEPT`) queries are accepted — multi-statement queries and `INTO OUTFILE`/`INTO DUMPFILE` are rejected. The query runs with the permissions of whoever triggered the flow run, against the project's own datasets — there's no separate connection or data source to configure. Unlike SQL Explorer, **no default row limit is applied** to a flow's `sql` node, so add your own `LIMIT` or a large result set can be slow to return.
</Warning>

<Note>
  Every `sql` node execution is recorded as an auditable query result against the flow run, and counts against the organization's query quota — a run made while that quota is exceeded fails with `Query quota exceeded for this account`.
</Note>

## Response

A terminal node that returns the upstream result, unchanged, to the flow's caller.

| Property | Category | Inputs | Outputs |
| - | - | - | - |
| `response` | terminal | `in` (required) | — |

```yaml theme={null}
- id: finish
  type: response
  label: Response
  config: {}
```

<Note>
  A flow may contain **at most one** top-level `response` node. Inside a `for_each` body, exactly **one** `response` node is required — it defines that iteration's result.
</Note>

## Output

A sink node — writes the upstream result to a dataset, webhook, or file.

| Property | Category | Inputs | Outputs |
| - | - | - | - |
| `output` | sink | `in` (required) | — |

| Attribute | Type | Default | Description |
| - | - | - | - |
| `kind` | string | — | One of `dataset`, `webhook`, `file` (required) |
| `dataset_alias` | string | `null` | Required when `kind: dataset` |
| `materialized` | bool | `false` | Reserved for a future materialized dataset write path — not yet supported |
| `url` | string | `null` | Required when `kind: webhook`. Must start with `http://` or `https://` |
| `path` | string | `null` | Required when `kind: file` |

```yaml theme={null}
- id: post_result
  type: output
  label: Send to webhook
  config:
    kind: webhook
    url: "https://hooks.example.com/flow-result"
```

```yaml theme={null}
- id: write_dataset
  type: output
  label: Write to dataset
  config:
    kind: dataset
    dataset_alias: "processed_orders"
```

<Warning>
  Setting `materialized: true` on a `kind: dataset` output currently fails validation — materialized dataset writes are not supported yet.
</Warning>

## Next Steps

* [Overview](/reference/flows/overview) - flow structure and core concepts
* [Templating & Expressions](/reference/flows/templating) - exactly how each node type consumes `inputs`/`parameters`
* [YAML Reference](/reference/flows/yaml-reference) - the complete flow document schema


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.