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

# Node Types: TRIGGER, DATA_SOURCE, AI, FILTER, OUTPUT

> Flowmatic workflows are composed of five node types: TRIGGER, DATA_SOURCE, AI, FILTER, and OUTPUT. Each node performs a specific transformation or action.

Every workflow in Flowmatic is built from a set of typed nodes, each responsible for a single, well-defined step in your automation pipeline. Nodes are connected by edges to form a directed graph, and each node type accepts a specific `data` configuration object and produces named output fields that downstream nodes can reference via template variables. This page documents all five node types in detail.

***

## Node Object Shape

Regardless of type, every node in your `nodes` array shares the same base structure:

```json title="Base node structure" theme={null}
{
  "id":   "myNode",
  "type": "NODE_TYPE",
  "data": { }
}
```

| Field  | Type   | Required | Description                                                                                                                                  |
| ------ | ------ | -------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`   | string | ✅ Yes    | A unique identifier within the workflow. This becomes the namespace for the node's template variable outputs (e.g., `{{myNode.someField}}`). |
| `type` | string | ✅ Yes    | One of `TRIGGER`, `DATA_SOURCE`, `AI`, `FILTER`, or `OUTPUT`.                                                                                |
| `data` | object | ✅ Yes    | Type-specific configuration. Use `{}` for node types with no required configuration.                                                         |

***

## Node Types

<Accordion title="TRIGGER — Pipeline Entry Point">
  ### What It Does

  The `TRIGGER` node is the **starting point** of every workflow. When you enqueue a run, Flowmatic begins execution at the `TRIGGER` node and fans out to all nodes connected to it via outgoing edges. Every valid workflow must contain exactly one `TRIGGER` node.

  The `TRIGGER` node performs no data transformation itself — its purpose is purely structural. It acts as the root of the directed graph, ensuring a single, unambiguous starting point for each execution.

  ### Configuration (`data`)

  The `TRIGGER` node requires no configuration.

  ```json title="TRIGGER node data" theme={null}
  {
    "data": {}
  }
  ```

  ### Outputs

  The `TRIGGER` node does not produce any output fields. It does not expose template variables for downstream nodes.

  ### Example

  ```json title="TRIGGER node in context" theme={null}
  {
    "id":   "trigger",
    "type": "TRIGGER",
    "data": {}
  }
  ```

  And the corresponding edge that connects it to the next node:

  ```json title="Edge from TRIGGER to DATA_SOURCE" theme={null}
  { "source": "trigger", "target": "ds" }
  ```

  <Note>
    A workflow with no `TRIGGER` node will fail validation at creation time. Make sure your graph has exactly one node of type `TRIGGER`.
  </Note>
</Accordion>

<Accordion title="DATA_SOURCE — Load CSV Data">
  ### What It Does

  The `DATA_SOURCE` node loads a previously uploaded CSV file and makes all of its rows available as a structured array to downstream nodes. Before using a `DATA_SOURCE` node, you must upload your CSV via `POST /api/uploads`, which returns an `uploadId`. That ID is what you provide to this node.

  At runtime, Flowmatic fetches the CSV associated with the given `uploadId`, parses it, and exposes a `rows` array where each element is an object whose keys correspond to the CSV column headers.

  ### Configuration (`data`)

  <ParamField body="uploadId" type="string" required>
    The ID of a previously uploaded CSV file, returned by `POST /api/uploads`. This tells Flowmatic which dataset to load for this execution.
  </ParamField>

  ```json title="DATA_SOURCE data object" theme={null}
  {
    "uploadId": "upl_abc123"
  }
  ```

  ### Outputs

  <ResponseField name="rows" type="array">
    An array of objects, one per CSV row. Each object's keys are the column headers from the CSV file, and the values are the corresponding cell values. Reference this output in downstream nodes as `{{nodeId.rows}}`.
  </ResponseField>

  For example, if your CSV looks like:

  ```csv title="customers.csv" theme={null}
  name,email,rating
  Alice,alice@example.com,5
  Bob,bob@example.com,3
  Carol,carol@example.com,4
  ```

  Then `{{ds.rows}}` will resolve to:

  ```json title="Resolved rows value" theme={null}
  [
    { "name": "Alice", "email": "alice@example.com", "rating": 5 },
    { "name": "Bob",   "email": "bob@example.com",   "rating": 3 },
    { "name": "Carol", "email": "carol@example.com",  "rating": 4 }
  ]
  ```

  ### Example

  ```json title="DATA_SOURCE node in context" theme={null}
  {
    "id":   "ds",
    "type": "DATA_SOURCE",
    "data": {
      "uploadId": "upl_abc123"
    }
  }
  ```

  Downstream nodes reference its output as `{{ds.rows}}`:

  ```json title="AI node referencing DATA_SOURCE output" theme={null}
  {
    "id":   "ai",
    "type": "AI",
    "data": {
      "prompt": "Analyze these customer rows: {{ds.rows}}. Identify high-value customers.",
      "output": [{ "name": "customers", "type": "array" }]
    }
  }
  ```

  <Tip>
    You can have multiple `DATA_SOURCE` nodes in a single workflow — each pointing to a different upload — if your pipeline needs to join or compare multiple datasets. Give each a distinct `id` so their `rows` outputs don't collide.
  </Tip>
</Accordion>

<Accordion title="AI — LLM Processing">
  ### What It Does

  The `AI` node sends data to Flowmatic's built-in large language model for intelligent processing. You describe what you want the model to do in plain English via the `prompt` field, and you declare the named output fields you expect the model to return via the `output` array. Flowmatic enforces the output schema and makes each declared field available to downstream nodes as a template variable.

  Use the `AI` node when you need to enrich, classify, summarize, transform, or generate content from your data — tasks that would be impractical to express as simple filter rules.

  ### Configuration (`data`)

  <ParamField body="prompt" type="string" required>
    A natural language instruction for the LLM. Describe the task you want the model to perform. You can embed template variables (e.g., `{{ds.rows}}`) to inject upstream data directly into the prompt at runtime.
  </ParamField>

  <ParamField body="output" type="array" required>
    An array of output field definitions. Each element declares a named field that the LLM is expected to return. Flowmatic uses this schema to parse and validate the model's response.

    Each element has:

    * `name` (string) — the field name, used in downstream template variables as `{{nodeId.name}}`
    * `type` (string) — the expected type: `"array"` or `"string"`
  </ParamField>

  ```json title="AI data object" theme={null}
  {
    "prompt": "Given the following customer rows: {{ds.rows}}, identify high-value customers. For each, extract their name, email, and rating. Also write a short personalized outreach message and return it as messageBody.",
    "output": [
      { "name": "customers",   "type": "array"  },
      { "name": "messageBody", "type": "string" }
    ]
  }
  ```

  ### Outputs

  The `AI` node exposes one template variable per entry declared in the `output` array. If the node has `id: "ai"` and declares outputs `customers` and `messageBody`, you reference them as:

  <ResponseField name="{{ai.customers}}" type="array">
    The array of objects returned by the LLM for the `customers` output field.
  </ResponseField>

  <ResponseField name="{{ai.messageBody}}" type="string">
    The string returned by the LLM for the `messageBody` output field.
  </ResponseField>

  ### Example

  ```json title="AI node in context" theme={null}
  {
    "id":   "ai",
    "type": "AI",
    "data": {
      "prompt": "Analyze the following rows: {{ds.rows}}. For each customer, return their name, email, and rating in an array called customers. Also produce a single messageBody string suitable for a promotional email.",
      "output": [
        { "name": "customers",   "type": "array"  },
        { "name": "messageBody", "type": "string" }
      ]
    }
  }
  ```

  Downstream nodes reference its outputs:

  ```json title="FILTER node referencing AI output" theme={null}
  {
    "id":   "f",
    "type": "FILTER",
    "data": {
      "source": "{{ai.customers}}",
      "expr":   "rating > 4"
    }
  }
  ```

  <Warning>
    The quality of the `AI` node's output depends heavily on how clearly you write the `prompt`. Be specific about the structure you expect. Ambiguous prompts can produce inconsistently shaped output arrays that cause downstream nodes to fail.
  </Warning>

  <Tip>
    You can reference any upstream node's output in the `prompt` — not just the immediately preceding node. For example, `{{ds.rows}}` and `{{otherAi.customers}}` can both appear in the same prompt string, as long as both nodes are upstream in the graph.
  </Tip>
</Accordion>

<Accordion title="FILTER — Rule-Based Row Filtering">
  ### What It Does

  The `FILTER` node takes an array from an upstream node and applies a boolean expression to each element, returning only the rows that match. This is the go-to node whenever you need to narrow a dataset before acting on it — for example, keeping only customers with a high rating, or only records with a specific status value.

  The filter expression is evaluated per-row using the row's fields as variables. You don't need to write code — the expression language is simple and human-readable.

  ### Configuration (`data`)

  <ParamField body="source" type="string" required>
    A template variable reference to the upstream array you want to filter. For example, `{{ai.customers}}` or `{{ds.rows}}`. This must resolve to an array at runtime.
  </ParamField>

  <ParamField body="expr" type="string" required>
    A boolean filter expression evaluated against each element of the `source` array. The expression has access to every field on the row object as a bare variable name. Rows for which the expression evaluates to `true` are included in the output `items` array.

    **Supported operators:** `>`, `<`, `>=`, `<=`, `==`, `!=`, `&&`, `||`, `!`

    **Examples:**

    * `rating > 4`
    * `status == 'active'`
    * `score >= 80 && region == 'US'`
    * `!opted_out`
  </ParamField>

  ```json title="FILTER data object" theme={null}
  {
    "source": "{{ai.customers}}",
    "expr":   "rating > 4"
  }
  ```

  ### Outputs

  <ResponseField name="items" type="array">
    The subset of elements from `source` for which `expr` evaluated to `true`. Each element retains all of its original fields. Reference this in downstream nodes as `{{nodeId.items}}`.
  </ResponseField>

  For example, if `{{ai.customers}}` contains three rows and two of them have `rating > 4`, then `{{f.items}}` will contain those two rows.

  ### Example

  ```json title="FILTER node in context" theme={null}
  {
    "id":   "f",
    "type": "FILTER",
    "data": {
      "source": "{{ai.customers}}",
      "expr":   "rating > 4"
    }
  }
  ```

  Downstream `OUTPUT` node iterating the filtered results:

  ```json title="OUTPUT iterating FILTER results" theme={null}
  {
    "id":   "out",
    "type": "OUTPUT",
    "data": {
      "forEach": "{{f.items}}",
      "to":      "{{item.email}}",
      "subject": "Exclusive offer for top customers",
      "body":    "Hi {{item.name}}, your rating of {{item.rating}} qualifies you for our VIP program."
    }
  }
  ```

  <Note>
    If the filter expression matches zero rows, the `FILTER` node outputs an empty `items` array. The downstream `OUTPUT` node will iterate zero times, sending no emails. This is expected behavior — no error is raised.
  </Note>
</Accordion>

<Accordion title="OUTPUT — Send Emails">
  ### What It Does

  The `OUTPUT` node is the terminal action node in a workflow. It iterates over an array of rows and sends one email per element. You configure the recipient, subject, and body using template variables — including `{{item.fieldName}}` to reference fields from the current row in the loop.

  Flowmatic's email delivery is handled automatically; you only need to provide the addressing and content. The `OUTPUT` node is typically the last node in a workflow graph (no outgoing edges required), though it may appear anywhere an action should occur.

  ### Configuration (`data`)

  <ParamField body="forEach" type="string" required>
    A template variable reference to the array you want to iterate over. One email is sent per element. For example, `{{f.items}}` or `{{ai.customers}}`. This must resolve to an array at runtime.
  </ParamField>

  <ParamField body="to" type="string" required>
    The recipient email address for each iteration. Typically uses `{{item.email}}` to dynamically address each row's owner. Must resolve to a valid email address at runtime.
  </ParamField>

  <ParamField body="subject" type="string" required>
    The email subject line. Supports template variables, including `{{item.fieldName}}` for per-row personalization (e.g., `"A message for {{item.name}}"`).
  </ParamField>

  <ParamField body="body" type="string" required>
    The email body text. Supports template variables and multi-line strings. Use `{{item.fieldName}}` to inject row-level data, and `{{nodeId.outputField}}` to inject workflow-level values produced by upstream nodes.
  </ParamField>

  ```json title="OUTPUT data object" theme={null}
  {
    "forEach": "{{f.items}}",
    "to":      "{{item.email}}",
    "subject": "{{item.name}}, here's your personalized update",
    "body":    "Hi {{item.name}},\n\n{{item.messageBody}}\n\nYour current rating is {{item.rating}}.\n\nBest regards,\nThe Team"
  }
  ```

  ### Outputs

  The `OUTPUT` node does not produce template variable outputs for downstream nodes. It is a terminal action — its purpose is to dispatch emails, not transform data.

  ### The `{{item.*}}` Variable

  Inside `to`, `subject`, and `body`, the special `{{item.*}}` namespace refers to the **current element** of the `forEach` array during each iteration of the loop. Every field present on the row object is accessible as `{{item.fieldName}}`.

  For instance, if `{{f.items}}` resolves to:

  ```json title="Resolved items array" theme={null}
  [
    { "name": "Alice", "email": "alice@example.com", "rating": 5, "messageBody": "You're a VIP!" },
    { "name": "Carol", "email": "carol@example.com", "rating": 5, "messageBody": "We value you!" }
  ]
  ```

  Then the `OUTPUT` node sends **two emails**: one to `alice@example.com` and one to `carol@example.com`, each with personalized subject and body content.

  ### Example

  ```json title="OUTPUT node in context" theme={null}
  {
    "id":   "out",
    "type": "OUTPUT",
    "data": {
      "forEach": "{{f.items}}",
      "to":      "{{item.email}}",
      "subject": "A special offer for {{item.name}}",
      "body":    "Hi {{item.name}},\n\nWe noticed your rating of {{item.rating}} — we'd love to offer you something special.\n\n{{item.messageBody}}\n\nThanks,\nThe Team"
    }
  }
  ```

  <Warning>
    Make sure the array referenced by `forEach` contains objects with an `email` field (or whichever field you use in `to`). If `{{item.email}}` resolves to an empty string or an invalid address, that iteration's email will fail and the run may be marked `FAILED`.
  </Warning>

  <Tip>
    You can mix `{{item.*}}` variables (row-scoped) and `{{nodeId.field}}` variables (workflow-scoped) in the same `body` string. For example, you might use `{{ai.messageBody}}` for a shared intro paragraph and `{{item.name}}` for per-recipient personalization.
  </Tip>
</Accordion>

***

## Node Type Quick Reference

<CardGroup cols={2}>
  <Card title="TRIGGER" icon="bolt">
    Starts the pipeline. No configuration required. Every workflow needs exactly one.
  </Card>

  <Card title="DATA_SOURCE" icon="database">
    Loads a CSV by `uploadId` and outputs a `rows` array. Reference as `{{nodeId.rows}}`.
  </Card>

  <Card title="AI" icon="brain">
    Sends data to an LLM with a natural language `prompt`. Outputs named fields defined in the `output` array.
  </Card>

  <Card title="FILTER" icon="filter">
    Filters an upstream array by a boolean `expr`. Outputs matching rows as `{{nodeId.items}}`.
  </Card>

  <Card title="OUTPUT" icon="envelope">
    Iterates a `forEach` array and sends one email per row using `to`, `subject`, and `body` templates.
  </Card>
</CardGroup>

***

## Template Variable Summary

| Pattern                | Where It's Used                       | Resolves To                              |
| ---------------------- | ------------------------------------- | ---------------------------------------- |
| `{{nodeId.rows}}`      | `DATA_SOURCE` output                  | All CSV rows as an array of objects      |
| `{{nodeId.fieldName}}` | `AI` output                           | A named field declared in `output` array |
| `{{nodeId.items}}`     | `FILTER` output                       | Filtered rows as an array                |
| `{{item.fieldName}}`   | Inside `OUTPUT` `to`/`subject`/`body` | Field value on the current loop element  |

***

## Next Steps

<CardGroup cols={2}>
  <Card title="Workflows" icon="diagram-project" href="/concepts/workflows">
    Learn how nodes and edges compose into a full workflow definition.
  </Card>

  <Card title="Runs" icon="circle-play" href="/concepts/runs">
    Understand the async run lifecycle and how to monitor per-node execution status.
  </Card>
</CardGroup>
