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

# Flow Nodes Reference

> Every Agent Flows node explained in plain language — what each node does, when to reach for it, its key settings, and the data it takes in and gives out.

This is the field guide to the **nodes** you drag onto the Agent Flows canvas. Each node does one job; you wire them together into a graph that compiles to a real LangGraph agent. Everything below is generated from the live node registry, so the names, settings, and ports match exactly what you see in the canvas.

<Note>
  New to Agent Flows? Read [Agent Flows](/admin-guide/agent-flows) for the canvas basics (creating, publishing, enabling a flow as a chat sub-agent). For **end-to-end example flows you can copy**, see the [Agent Flows Cookbook](/admin-guide/agent-flows-cookbook). This page is the reference for the individual nodes those recipes use.
</Note>

## How data flows between nodes

Two different things connect your nodes, and it helps to keep them separate in your head:

* **Edges = order.** An edge (the line you draw from one node to the next) decides *when* a node runs. `start → A → B → end` means run A, then B.
* **Bindings = data.** A binding decides *what value* feeds a node's input port. You bind an input to one of these sources:

| Bind an input to…         | Path you'll see               | What it is                                                                                                                                 |
| ------------------------- | ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| **Chat message**          | `$.params.message`            | The user's core request, forwarded by the chat agent when it launches the flow (or the message you supply in the Run tab).                 |
| **A run parameter**       | `$.params.<name>`             | Any value provided when the flow is run — from the Run tab, or mapped from the user's request by the chat agent when it launches the flow. |
| **Another node's output** | `$.nodes.<id>.outputs.<port>` | A value produced earlier in the flow (e.g. `$.nodes.kg.outputs.text`).                                                                     |
| **An uploaded file slot** | `$.files.<slot>`              | Files attached to the run, or the conversation's uploads bound in when the chat agent launches the flow.                                   |

<Tip>
  **You rarely type these paths by hand.** When you connect two nodes, the canvas **auto-wires** compatible ports for you. To send one specific value, **drag from an output port to an input port**. Prompt fields also accept inline templates — `{{ params.message }}`, `{{ inputs.context }}`, `{{ nodes.<id>.outputs.text }}` — so you can weave values straight into the text.
</Tip>

<Note>
  A node with **two incoming edges waits for both** before it runs. That's how you fan two parallel branches back into one node (e.g. a Knowledge Graph branch and a Web research branch both feeding one `LLM generate`). You only need the explicit **Join** node when you fanned out with **Map**.
</Note>

Nodes fall into five categories in the palette: **Control**, **Data**, **LLM**, **Agents**, and **Human**. Here's every one.

***

## Control nodes

The plumbing: where a flow starts and ends, and how it branches, repeats, and fans out. Control nodes don't call a model — they're fast and deterministic.

| Node (`type`)                       | What it does                                                                    | When to use                                         | Inputs → Outputs                                       |
| ----------------------------------- | ------------------------------------------------------------------------------- | --------------------------------------------------- | ------------------------------------------------------ |
| **Start** (`control.start`)         | The entry point. Exposes the run's params and uploaded files.                   | Every flow has exactly one.                         | — → `params`, `files`                                  |
| **End** (`control.end`)             | The exit point. Collects the flow's final result.                               | Every flow has exactly one; bind its `result`.      | `result` → `result`                                    |
| **Condition** (`control.condition`) | Tests one value and produces true/false to route on.                            | Send the run down different paths based on a value. | (reads a state path) → `result` (bool), `value`        |
| **Merge** (`control.merge`)         | Rejoins branches after a Condition; forwards the branch that actually ran.      | After a Condition fan-out, to get back to one path. | one input per branch → `value`, `values`               |
| **Map** (`control.map`)             | Fans a **list** out to parallel branches — one per item.                        | Do the same step over many items at once.           | `items` (required) → `items`, `count`, `item`, `index` |
| **Join** (`control.join`)           | Collects the results of every Map branch once they all finish.                  | Always paired with a Map.                           | — → `results`, `count`, `failed_indexes`               |
| **Loop** (`control.loop`)           | A bounded counter that lets a cycle repeat a fixed number of times, then stops. | Sequential repeat (e.g. revise-until-approved).     | — → `iteration`, `exhausted`                           |

**Condition** (`control.condition`) — set **Value path** to the value you're testing (e.g. `$.nodes.kg.outputs.row_count`), an **Operator** (`eq`, `ne`, `gt`, `gte`, `lt`, `lte`, `in`, `not_in`, `contains`, `truthy`, `falsy`), and a **Comparison value** when the operator needs one. The edges leaving a Condition decide which branch runs on true vs false.

**Map** (`control.map`) — set **Body node** to the node that runs once per item, and **Join node** to the `control.join` that collects the results. Inside the body, read the current item with `$.nodes.<map_id>.outputs.item` (and `.index`). The list you bind to **items** is fanned out in parallel.

**Loop** (`control.loop`) — set **Max iterations** (1–1000). Every cycle must pass through the Loop node; once the count hits the max, `exhausted` flips to true so you can route out of the loop.

<Note>
  **Start**, **End**, **Merge**, and **Join** have no settings to fill in — they just need to be wired. **End** is where the flow's answer comes from, so always bind its `result` to whatever produced the final value.
</Note>

***

## Data nodes

These work with your content: query the knowledge graph, index uploaded files, and render documents.

| Node (`type`)                                 | What it does                                                                                               | When to use                                                                    | Inputs → Outputs                                                                                  |
| --------------------------------------------- | ---------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------- |
| **Knowledge Graph Query** (`knowledge_graph`) | Asks your organization's knowledge graph a question; returns rows and (optionally) a cited written answer. | Grounded Q\&A over your own data (projects, deliverables, contracts, people…). | `query`, `custom_instructions` → `rows`, `row_count`, `entities`, `cypher`, `text`, `evidence`, … |
| **Index uploads** (`data.index_files`)        | Parses, chunks, and embeds uploaded files so later nodes can use their content.                            | When a flow starts from uploaded documents.                                    | `message_file_ids` (required) → `indexed`, `skipped`, `indexed_count`, `documents`                |
| **Generate DOCX** (`data.generate_docx`)      | Renders text or structured sections into a downloadable Word document.                                     | Turn an answer into a polished `.docx` deliverable.                            | `title`, `sections`, `text` → `file_path`, `filename`, `size_bytes`, `artifact_key`               |
| **Generate PPTX** (`data.generate_pptx`)      | Renders sections into a downloadable PowerPoint presentation (one slide per section).                      | Turn an answer into a `.pptx` deck.                                            | `title`, `sections`, `text` → `file_path`, `filename`, `size_bytes`, `artifact_key`               |
| **Generate file** (`data.generate_file`)      | Renders content into a chosen format — Word, PowerPoint, PDF, or HTML — reusing the same generators.       | One node when the output format is configurable (or PDF/HTML).                 | `title`, `sections`, `content`, `text` → `file_path`, `filename`, `size_bytes`, `artifact_key`    |

**Knowledge Graph Query** (`knowledge_graph`) — the workhorse for answering from your own data. Key settings:

* **Output mode** — `data` returns rows only; **`report`** additionally synthesizes a cited narrative on the `text` output (with `evidence`). Pick `report` when you want a written answer, not just a table.
* **Query** — the question. Leave it templated or bind the **`query`** input to **Chat message** so the user's question drives it.
* **On clarify** — what to do when the question is too vague to scope: `fail` (stop), `interrupt` (pause for a human), or `skip` (continue with empty rows).
* **Fail on empty**, **Include graph context**, **Custom instructions**, and report-tuning options (**Output instructions**, preview/token caps) are available for finer control.

<Tip>
  Large result sets (over \~1,000 rows) are automatically exported to a CSV file artifact and surfaced on the `rows_file` output, so the run stays lightweight.
</Tip>

**Index uploads** (`data.index_files`) — bind **message file ids** to an uploaded file slot (`$.files.<slot>`). It indexes the files and also emits short text previews on `documents` that downstream LLM nodes (like `llm.extract`) can read directly.

**Generate DOCX** (`data.generate_docx`) — bind **sections** (a list of structured `{heading, content}` objects) for a multi-section document, or bind **text** for a single-section body (e.g. a Knowledge Graph report). Set the **Document title**, optionally **Include table of contents**, and choose a **Theme**. To brand the output, pick a **Document Template** by name (one of your registered [Document Templates](/admin-guide/document-templates)); the document then inherits that template's styles, headers/footers, and logo.

**Generate PPTX** (`data.generate_pptx`) — the slide equivalent of Generate DOCX: bind **sections** (one slide each) or **text**, set the **Title** and **Theme**, and optionally pick a **PPTX template** by name for branded slide masters.

**Generate file** (`data.generate_file`) — a single node with a **format chooser**: **Word (`.docx`)**, **PowerPoint (`.pptx`)**, **PDF**, or **HTML**. It reuses the same generators as the dedicated DOCX/PPTX nodes. The **Document Template** selector appears only for the `docx` and `pptx` formats. For `pdf`/`html`, bind **content** to the format-native source (LaTeX for PDF, HTML for HTML); for `docx`/`pptx`, bind **sections** (or **text**) as you would for the dedicated nodes.

<Note>
  Markdown that shows up in generated content — `#`/`##`/`###` headings, `**bold**`/`*italic*`, and `-`/`1.` lists — is rendered as **real Word/PPTX styling** rather than left as literal markers in the document.
</Note>

<Note>
  The hidden phases of Knowledge Graph Query — `kg.scope`, `kg.retrieve`, `kg.report` — are covered under [Advanced / hidden nodes](#advanced--hidden-nodes) below. You don't need them for normal flows.
</Note>

***

## LLM nodes

Single-call language-model steps. Each makes **one** model call from a prompt (plus optional `context` input) — predictable cost, no looping. All three accept `{{ params.* }}`, `{{ inputs.* }}`, and `{{ nodes.<id>.outputs.* }}` templates in their prompts.

| Node (`type`)                     | What it does                                                            | When to use                                                  | Inputs → Outputs                    |
| --------------------------------- | ----------------------------------------------------------------------- | ------------------------------------------------------------ | ----------------------------------- |
| **LLM generate** (`llm.generate`) | Writes free-form text from a prompt. The general-purpose writer.        | Summarize, fuse sources, draft prose, rewrite.               | `context` → `text`                  |
| **LLM extract** (`llm.extract`)   | Pulls structured, typed fields out of content into a schema you define. | When downstream needs machine-readable data (lists, fields). | `context` → `data` (object)         |
| **LLM classify** (`llm.classify`) | Picks exactly one label from a list you define.                         | Routing and tagging.                                         | `context` → `category`, `reasoning` |

**LLM generate** (`llm.generate`) — set the **Prompt** (required) and optionally a **System prompt**. Bind **context** to whatever you want appended to the prompt (e.g. a Knowledge Graph report). Output lands on `text`.

**LLM extract** (`llm.extract`) — set the **Prompt** plus an **Output schema**: a map of `{name: {type, description, required}}` where `type` is `string | number | integer | boolean | array | object`. The model is forced to return exactly that shape on the `data` output. Example schema:

```json theme={null}
{ "requirements": { "type": "array", "description": "All RFP requirements", "required": true } }
```

**LLM classify** (`llm.classify`) — set the **Prompt** and a list of **Categories** (at least two, unique). The output `category` is guaranteed to be one of your labels, with a one-line `reasoning`. Wire `category` into a **Condition** to route.

<Note>
  All three take an optional **Model** (defaults to the global reasoning model), an **Output format** hint, and **Include graph schema** (adds a compact knowledge-graph schema to the prompt when the step reasons over graph data).
</Note>

***

## Agent nodes

These run a **bounded, model-driven loop** — the model decides its own steps using tools until it finishes or hits its budget. Because they loop, every agent node has a **budget** (max model calls, tool calls, tokens, wall-seconds) so it can never run unchecked. Unset budget fields fall back to sensible defaults.

| Node (`type`)                                        | What it does                                                                                                   | When to use                                                 | Inputs → Outputs                                                        |
| ---------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------- | ----------------------------------------------------------------------- |
| **Knowledge research agent** (`agent.deep_research`) | Self-directed, multi-step investigation over your **internal** knowledge (graph + ingested documents). No web. | Deep questions over internal data needing several queries.  | `context` → `status`, `text`, `structured`, `usage`                     |
| **Web research agent** (`agent.web_research`)        | Self-directed investigation over the **live web** (Tavily), with cited source URLs.                            | Current or external information.                            | `context` → `status`, `text`, `structured`, `usage`                     |
| **Orchestrator agent** (`agent.orchestrator`)        | A coordinator that calls **other nodes as tools** and decides the path at runtime.                             | Open-ended tasks where you can't predict the steps.         | `context` → `status`, `text`, `structured`, `plan`, `evidence`, `usage` |
| **MCP action** (`mcp.action`)                        | Runs a bounded loop over the tools of **one MCP server** (Gmail, HubSpot, time, …).                            | Take actions or fetch data through a connected integration. | `context` → `status`, `error`, `result`, `usage`                        |

**Knowledge research agent** (`agent.deep_research`) — set the **Research goal** (templatable; use `{{ params.message }}` to use the chat message). By default it uses the proven internal-knowledge tool set (knowledge graph + Cypher); you can override **Tool groups** or add one **MCP** server. **Report format** `structured` returns a typed `{title, summary, sections}` report. Defaults: 40 model calls / 80 tool calls / 1800s.

**Web research agent** (`agent.web_research`) — set the **Research goal**. It runs live web searches via Tavily and grounds every claim in cited URLs. Leave **Tavily API key** blank to use the org-level `TAVILY_API_KEY` configuration setting (recommended). Defaults are lighter than internal research: 30 model calls / 60 tool calls / 900s.

**Orchestrator agent** (`agent.orchestrator`) — set the **Goal** and **Allowed nodes** (the registry node types it may call as tools, e.g. `knowledge_graph`, `agent.web_research`, `data.generate_docx`). At runtime it plans with a todo list and chooses which nodes to call. Outputs include the final `text`, the `plan` it followed, and `evidence` (citations from report-mode node tools). Optional **Require plan approval** pauses for a human to approve the plan first. Defaults: 20 model calls / 30 tool calls / 900s.

**MCP action** (`mcp.action`) — set the **MCP server** and a **Task instruction**. Choose **Auth source** (`user` = the run-user's connection, `org` = organization integration, `auto` = user first then org), optionally **whitelist tools**, and set **Allow writes** if the task should mutate external systems. Write-capable tools are flagged in the tool list; when **Allow writes** is off, the builder warns which selected write tools would be **dropped at run time** — they are stripped so a read-only step can never mutate anything, and the run is reported as a failure rather than a false success if the task then can't complete. **On error** controls failure handling: `fail` (halt), `error_port` (emit the error envelope for conditional routing on `status`), or `continue`. Defaults: 10 model calls / 15 tool calls / 300s.

<Warning>
  MCP servers that need OAuth (Google, Slack, HubSpot…) must be **connected first** under Admin → MCP Servers. No-auth servers (e.g. time, sequential-thinking) work immediately.
</Warning>

***

## Human nodes

| Node (`type`)                     | What it does                                                           | When to use                                       | Inputs → Outputs                                          |
| --------------------------------- | ---------------------------------------------------------------------- | ------------------------------------------------- | --------------------------------------------------------- |
| **Human review** (`human.review`) | Pauses the run so a person can approve, edit, or reject, then resumes. | Anything that needs sign-off before it finalizes. | `payload` → `approved`, `decision`, `comment`, `response` |

**Human review** (`human.review`) — bind **payload** to the data you want the reviewer to see (e.g. a draft). When the run reaches this node it pauses and surfaces the item in the **Agent Inbox**. On approval the flow resumes; on rejection you can route back to a revise step. Set a **Review title** and **Reviewer message** for context, and choose the **Review layout** (`inline_summary` or `artifact_download`).

**Reviewer note.** On the review card — whether it appears in the canvas **Run** panel, the in-chat flow-run panel, or the **Agent Inbox** (it's one shared card) — the reviewer can add a **note**. The note is **optional when approving** and **required when rejecting**, so a rejection always comes back with a reason.

**Outputs.** Alongside the existing `approved` (boolean) and `response` (object), the node exposes two first-class ports for the reviewer's decision:

| Output     | Type    | Value                                                    |
| ---------- | ------- | -------------------------------------------------------- |
| `approved` | boolean | `true` on approve, `false` on reject                     |
| `decision` | string  | `"approved"` or `"rejected"`                             |
| `comment`  | string  | The reviewer's note (empty if they approved without one) |
| `response` | object  | The full review payload (decision + edits)               |

Downstream nodes consume the note by binding `{{ nodes.<review_id>.outputs.comment }}` into a prompt — e.g. a revise / LLM step that applies the feedback — and can route on `{{ nodes.<review_id>.outputs.decision }}` (or the existing `approved`). This feeds the reviewer's feedback straight into the revise loop or the next LLM node.

<Note>
  Human review requires a **checkpointed run** (the engine has to be able to pause and resume) — this is the default when a flow is published and run.
</Note>

***

## Choosing the right node

A few decisions come up again and again. Here's how to pick.

### Which LLM node — classify, extract, or generate?

| You want…                                                     | Use              | Output                                 |
| ------------------------------------------------------------- | ---------------- | -------------------------------------- |
| To pick **one label** from a fixed set (then route on it)     | **LLM classify** | `category` (always one of your labels) |
| **Structured data** (fields, lists) the next node can consume | **LLM extract**  | `data` (matches your schema)           |
| **Free text** — a summary, a fused answer, a draft            | **LLM generate** | `text`                                 |

Rule of thumb: *classify* when you'll branch on the result, *extract* when a downstream node (Map, Generate DOCX, a table) needs typed values, and *generate* for anything that should read as prose.

### Map (parallel) vs Loop (sequential)

* **Map** runs the same step over **many items at the same time** — fast, and each branch is independent (e.g. author every section of a document in parallel). Always pair it with **Join** to collect the results.
* **Loop** repeats a step **one pass at a time**, up to a max count, where each pass can depend on the last (e.g. revise-until-approved, or a fixed number of retries).

Use Map for throughput over a list; use Loop for an iterative cycle that has to happen in order.

### Condition + Merge (branch, then rejoin)

Use them as a pair: a **Condition** evaluates a value and routes the run down one of several branches; each branch does its own work; a **Merge** converges them back to a single path (it forwards the one branch that actually ran). Bind one input on the Merge per upstream branch. A common shape: classify → Condition → (branch A | branch B) → Merge → end.

### Knowledge Graph Query vs Knowledge research vs Web research vs Orchestrator

All four can "answer a question," but they trade off determinism, depth, and source:

| Node                                                 | Source                     | Style                                                   | Reach for it when…                                                                                               |
| ---------------------------------------------------- | -------------------------- | ------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
| **Knowledge Graph Query** (`knowledge_graph`)        | Internal graph             | One deterministic scope→retrieve(→report) pass          | You want a predictable, single-hop grounded answer over your own data. **Best default.**                         |
| **Knowledge research agent** (`agent.deep_research`) | Internal graph + documents | Self-directed, multi-step, plans and branches           | The question needs several queries / a deep dig over internal data — no web.                                     |
| **Web research agent** (`agent.web_research`)        | Live web (Tavily)          | Self-directed, cited URLs                               | You need current or external information.                                                                        |
| **Orchestrator agent** (`agent.orchestrator`)        | Whatever nodes you allow   | Decides the path at runtime, calls other nodes as tools | The task is open-ended and you can't predict which sources/steps are needed (it can combine graph + web + docx). |

Rule of thumb: **deterministic single question → Knowledge Graph Query**; **deep internal investigation → Knowledge research agent**; **external/current → Web research agent**; **unpredictable, multi-tool → Orchestrator agent**. For a *fixed* "graph **and** web" answer that always runs both, prefer two parallel nodes feeding one `LLM generate` (deterministic, predictable cost) over an Orchestrator — see [Cookbook Recipe 3](/admin-guide/agent-flows-cookbook).

***

## Advanced / hidden nodes

`kg.scope`, `kg.retrieve`, and `kg.report` are the **internal phases** of Knowledge Graph Query, split into separate nodes. They're **hidden from the palette** — new flows can't add them — because the single **Knowledge Graph Query** node does all three phases for you. They remain registered and runnable only so older published flows that referenced them keep working. You won't need them: use **Knowledge Graph Query** instead.

***

## Where to next

* **[Agent Flows Cookbook](/admin-guide/agent-flows-cookbook)** — validated, end-to-end example flows (and the patterns behind them) built from these nodes.
* **[Agent Flows](/admin-guide/agent-flows)** — the canvas basics: creating, validating, publishing, and enabling a flow as a chat sub-agent.
