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

# Workflow node reference: control flow and integration nodes

> Configure the Start, REST API Call, FlowX Database, Condition, Start and End Parallel, Script, Subworkflow, and End nodes in Integration Designer workflows.

This page describes the control flow and integration nodes you use in [Integration Designer workflows](https://docs.flowx.ai/5.9/docs/platform-deep-dive/integrations/integration-workflows.md). For every node type, see the [workflow nodes overview](https://docs.flowx.ai/5.9/docs/platform-deep-dive/integrations/integration-workflows.md#workflow-nodes-overview).

## Start node

The Start node is the mandatory first node in any workflow. It defines the input data model and passes this data to subsequent nodes.

> [!TIP]
>
> Define all required input fields in the Start node to ensure data mapping from processes or user tasks.

> [!NOTE]
>
> Use [Workflow Data Models](https://docs.flowx.ai/5.9/docs/platform-deep-dive/integrations/workflow-data-models.md) to define structured input and output parameters at the workflow level. Input parameters automatically pre-fill the Start Node from the data model.

### External triggers

SaaS · 5.12

> [!NOTE]
>
> **Available on SaaS with FlowX.AI 5.12.** This feature is live on managed (SaaS) deployments now. Self-hosted deployments will receive it with the next LTS release family.

A workflow normally runs when something calls it: a process, a parent workflow, or the run button. Turn on an external trigger and it starts on its own, when a webhook is called or an email arrives.

Open the **Start** node, expand **External Triggers**, switch **Enable trigger** on, then choose **Webhook** or **Email** and pick the data source that receives the event.

![Start node with External Triggers enabled, a webhook data source selected, and the Start payload showing the webhookMessage schema](https://s3.eu-west-1.amazonaws.com/docx.flowx.ai/5.9/start_node_trigger_example.png)

> [!NOTE]
>
> External triggers are available on Output Focused workflows only. Configuring one on another workflow type is rejected.

> [!NOTE]
>
> To run a workflow on a schedule on SaaS, add a schedule to its Start node. See [How do I run a workflow on a schedule?](#how-do-i-run-a-workflow-on-a-schedule). Self-hosted deployments get workflow schedules with the next LTS release. Until then, create a process with a Timer Start Event and call the workflow from it with a **Start Integration Workflow** action. See [Scheduled processes](https://docs.flowx.ai/5.9/docs/projects/runtime/scheduled-processes.md).

**Before you can pick a webhook**, the webhook data source has to be set to **Start a Process/Workflow**. A data source set to **Resume a Waiting Process** is refused, and because Webhook Behavior cannot be changed after the first save, a resume webhook can never be reused here: create a new one. See [Webhook Behavior](https://docs.flowx.ai/5.9/docs/platform-deep-dive/integrations/incoming-webhooks.md#webhook-behavior).

**The event lands in the data model.** Enabling the trigger adds an entity for the incoming event, and turning the trigger off removes it again, so switch it on before mapping anything downstream.

| Trigger | Entity | Contents |
| - | - | - |
| Webhook | `webhookMessage` | `dateTime`, `payload`, `headers` |
| Email | `emailMessage` | See the [email data schema](https://docs.flowx.ai/5.9/docs/platform-deep-dive/integrations/email-trigger.md#email-data-schema) |

`payload` holds the request body as sent, nested structure intact. `headers` holds each header as a **list** of strings, so a single-valued header still arrives as a one-element list.

A script node can then read it directly:

```javascript theme={"dark"}
var msg = input.webhookMessage;
var order = msg.payload;
output.orderSummary = order.client + " wants " + order.suma + " " + order.detalii.moneda;
```

**Activate the trigger** before it accepts anything. Go to **Runtime** → **Manage Triggers** and activate it there. The list also shows how many resources each trigger is bound to.

![Manage Triggers listing webhook triggers with their state and the resources each is used in](https://s3.eu-west-1.amazonaws.com/docx.flowx.ai/5.9/manage_triggers_5.9.x.png)

> [!WARNING]
>
> One data source can be bound to several workflows, and a single incoming event then starts **all** of them. The Manage Triggers list shows this as a resource count above one. Bind a data source to a second workflow only when you mean to run both.

> [card] [Incoming webhooks](https://docs.flowx.ai/5.9/docs/platform-deep-dive/integrations/incoming-webhooks.md)
>
> Creating a webhook data source, Webhook Behavior, providers, the URL and its API key

> [card] [Email trigger](https://docs.flowx.ai/5.9/docs/platform-deep-dive/integrations/email-trigger.md)
>
> Creating an email trigger data source, filtering, polling, and the email data schema

### How do I run a workflow on a schedule?

SaaS · 6.0

> [!NOTE]
>
> **Available on SaaS with FlowX.AI 6.0.** This feature is live on managed (SaaS) deployments now. Self-hosted deployments will receive it with the next LTS release family.

Add a schedule to the workflow's **Start** node, then activate it in **Runtime** → **Runtime Settings** → **Schedules**. From then on the workflow starts by itself at each fire time, with no process or caller involved.

Schedules are available on Output Focused workflows. A workflow can have up to 50 schedules, and each one needs a name that is unique on that workflow.

**1. Add a schedule**

Open the **Start** node, expand **Schedules**, and click **Add schedule**. Click the new schedule card to open the **Schedule Workflow** dialog, and give the schedule a **Name**.

**2. Set when it fires**

Choose a **Timer Type**:

* **Date** fires once, at the **Fire at** date and time, entered in your local time.
* **Cycle** fires repeatedly. Pick a **Format**, **ISO** or **Cron**, then build the expression with **Guided** fields or type it under **Expression**.

The dialog previews the next fire times in your local time zone. Check them, then click **Save**.

**3. Activate the schedule**

Go to **Runtime** → **Runtime Settings** → **Schedules**, filter **Origin** by **Workflow**, and click **Activate** on the schedule. The workflow must be part of the project's active build.

> [!WARNING]
>
> Saving a schedule or publishing a build never activates it. A schedule fires only after you activate it on the **Schedules** page.

**Which cycle format should I use?**

| Format | How it fires | Example | Rules |
| - | - | - | - |
| **ISO** | Repeats an ISO 8601 interval a set number of times, or indefinitely | `R5/P1D`: five times, once a day | The interval must be longer than zero, in whole seconds. A **Start Time**, if you set one, must be in the future. |
| **Cron** | Fires on a six-field cron schedule: second, minute, hour, day of month, month, day of week | `0 */5 * * * ?`: every five minutes | Cron is read in UTC. Set an **Active window** with a **Start** and an **End**: the schedule fires only between them. Macros such as `@daily` and `@hourly` also work. |

**What does a scheduled run receive?** A scheduled run doesn't use the Start node's test payload. Its input holds three fields that you can map like any other input:

| Field | Contents |
| - | - |
| `firedAt` | When the run actually started, as an ISO 8601 timestamp |
| `scheduledFor` | When the schedule was due, as an ISO 8601 timestamp |
| `triggerName` | The name of the schedule that started the run |

**Where do scheduled runs show up?** In [Workflow Instances](https://docs.flowx.ai/5.9/docs/projects/runtime/workflow-instances.md), with the origin **Schedule**. The run details name the schedule that started it.

**How do I stop a schedule?** To pause it, click **Deactivate** on the **Schedules** page, and click **Activate** to resume it. To remove it for good, delete it from its card on the **Start** node. A deleted schedule stops firing immediately, and the deletion can't be undone.

***

## REST API Call node

Enables communication with external systems via REST API calls. Supports GET, POST, PUT, PATCH, and DELETE methods. Endpoints are selected from a dropdown, grouped by system.

* **Params**: Configure path, query, and header parameters.
* **Input/Output**: Input is auto-populated from the previous node; output displays the API response.

> [!TIP]
>
> You can test REST API Call nodes independently to validate connections and data retrieval.

### When does a REST API Call node take its failure exit?

A REST API Call node has a success exit and a failure exit. It takes the success exit only for a `2xx` response without errors. Everything else takes the failure exit:

* a non-`2xx` response;
* a timeout, stored as status `408` with `data.timeout: true` (set the limit with **Timeout** on the node's **Settings** tab, default `10000` ms);
* a connection error;
* a response body declared as JSON or XML that can't be parsed.

With **Allow Retry** on, a `408`, `429`, `500`, `502`, `503`, or `504` is retried first, and the node takes the failure exit only once the retries are used up.

> [!WARNING]
>
> If the call fails and the failure exit isn't connected, the workflow instance fails with `Workflow is missing outgoing sequence.` It doesn't fall through to the success exit.

On both exits the node writes its result under its response key: `data` (the parsed body), `metadata` (`statusCode` and `headers`), `hasError` and `errorMessage`. `hasError` only flags connection and parsing errors: a `4xx` or `5xx` with a readable body has `hasError: false` even though the node took the failure exit, and on a timeout it isn't set. To branch on the outcome, use the exits or `metadata.statusCode`, not `hasError`.

Errors while building the request, such as a failed input mapping or a missing endpoint, don't use the failure exit. They fail the workflow instance.

***

## FlowX database node

Allows you to read and write data to the FlowX Database within your workflow.

> [card] [FlowX Database Documentation](https://docs.flowx.ai/5.9/docs/platform-deep-dive/integrations/flowx-database.md)
>
> Learn more about FlowX Database operations, collections, and usage

***

## Condition (Fork) node

Evaluates logical conditions (JavaScript or Python) to direct workflow execution along different branches.

* **If/Else**: Routes based on condition evaluation.
* **One branch per run**: The node follows the first branch whose condition passes, or the Else branch. To run branches at the same time, use the [Start Parallel and End Parallel nodes](#start-parallel-and-end-parallel-nodes).

> [!NOTE]
>
> Use fork nodes to implement business rules, error handling, or multi-path logic.

### Available bindings

Condition expressions have access to the following data bindings:

| Binding | Description |
| - | - |
| `input` | Workflow input data from the previous node |
| `additionalData.securityDetails` | Current user's identity (username, identifier, details including roles, groups, and attributes from 5.7.0 onward) |
| `additionalData.applicationConfiguration` | Configuration parameters from the app build |

### Condition examples

> [tab] JavaScript
>
> ```javascript theme={"dark"}
> // Route based on user identity
> additionalData.securityDetails.username === 'admin@company.com'
>
> // Route based on configuration parameter
> additionalData.applicationConfiguration.featureEnabled === true
>
> // Combine input data with config parameters
> input.amount > additionalData.applicationConfiguration.approvalThreshold
>
> // Check user and input together
> input.priority === 'high' && additionalData.securityDetails.details.email.endsWith('@company.com')
> ```

> [tab] Python
>
> ```python theme={"dark"}
> # Route based on user identity
> additionalData["securityDetails"]["username"] == "admin@company.com"
>
> # Route based on configuration parameter
> additionalData["applicationConfiguration"]["featureEnabled"] == True
>
> # Combine input data with config parameters
> input["amount"] > additionalData["applicationConfiguration"]["approvalThreshold"]
>
> # Check user and input together
> input["priority"] == "high" and additionalData["securityDetails"]["details"]["email"].endswith("@company.com")
> ```

> [!TIP]
>
> For details on the `securityDetails` and `applicationConfiguration` structure, see [Extracting additional data in business rules](https://docs.flowx.ai/5.9/docs/building-blocks/actions/business-rule-action/extracting-additional-data.md).

### Calling reusable functions in conditions

SaaS · 5.11

> [!NOTE]
>
> **Available on SaaS with FlowX.AI 5.11.** This feature is live on managed (SaaS) deployments now. Self-hosted deployments will receive it with the next LTS release family.

Condition expressions can call the [reusable functions](https://docs.flowx.ai/5.9/docs/building-blocks/reusable-resources/reusable-functions.md) defined in your project, so branch routing can reuse the same business logic as your processes. Call a function inside a condition using the standard syntax:

> [tab] JavaScript
>
> ```javascript theme={"dark"}
> // Route on the result of a reusable function
> functions.lending.isEligible(input.applicant) === true
> ```

> [tab] Python
>
> ```python theme={"dark"}
> # Route on the result of a reusable function
> functions.lending.isEligible(input["applicant"]) == True
> ```

Each branch condition can call functions, and the node resolves the functions referenced across all of its branches together. Resolution follows the same rules as the Script node: calls resolve from the workflow's build, a function that calls another function resolves its full call chain, and cross-app calls are matched by app name.

> [!WARNING]
>
> As with Script nodes, reusable functions resolve only in a build context. A draft or test run with no build resolves no functions.

***

## Start Parallel and End Parallel nodes

Run several steps of a workflow at the same time with a pair of nodes: **Start Parallel** splits the flow into branches that all run at once, and **End Parallel** waits for every branch, merges what the branches wrote, and continues. In the node palette they sit under **Flow Control** as **Start Parallel Path** and **End Parallel Path**. Dropped on the canvas, they are named **Start Parallel** and **End Parallel**.

These nodes are the only way to run branches concurrently. A [Condition (Fork) node](#condition-fork-node) evaluates its conditions and follows one branch.

<video controls src="https://s3.eu-west-1.amazonaws.com/docx.flowx.ai/5.x/parallel_wf.mp4" autoPlay muted loop />

### How do I run steps in parallel?

**1. Add a Start Parallel node**

Drag **Start Parallel Path** from the **Flow Control** group onto the canvas and connect the previous node to it.

**2. Create one sequence per branch**

Draw a sequence from the Start Parallel node to the first node of each branch. Every outgoing sequence is a branch. A branch needs at least one node: the Designer does not let you connect a Start Parallel node straight to an End Parallel or End Flow node.

**3. Build each branch**

Add the nodes each branch needs: REST endpoints, scripts, FlowX Database operations, AI nodes, subworkflows, or another Start Parallel node for nested branches.

**4. Close every branch on the same End Parallel node**

Connect the last node of every branch to one **End Parallel Path** node, then connect that node to whatever comes next. An End Parallel node allows a single outgoing sequence.

At runtime all branches run concurrently. The End Parallel node completes when the slowest branch reaches it, so the branch section takes as long as its longest branch, not the sum of the branches.

### What data does each branch see?

Each branch starts with a **deep copy** of the workflow data as it was when the Start Parallel node ran. Branches work on independent copies: a branch cannot read what another branch writes while they run.

### How are the branches merged?

When a branch reaches the End Parallel node, the keys that branch wrote are merged into the parent data with a recursive deep merge, in the order the branches finish:

* Keys written by only one branch are kept.
* A key written by two branches ends up with the value from the branch that finished last.
* Nested objects are merged key by key, so only the conflicting leaf values are overwritten.
* Arrays and all other non-object values are replaced whole. There is no element-level merge, so two branches cannot each fill a different element of the same array.

Because the merge order depends on which branch finishes last, two branches that write the same key give a non-deterministic result.

> [!TIP]
>
> Have each branch write to its own key, for example `apiResultA`, `apiResultB` and `apiResultC`, and combine them in a Script node after the End Parallel node.

### What happens when a branch fails?

* A node that fails inside a branch follows the same rules as anywhere else in the workflow. If its failure exit is connected, the branch continues on that path and must still reach the End Parallel node. If the failure exit is not connected, the workflow instance fails.
* When the instance fails, the other branches stop at the next node they reach. Nothing they already executed is rolled back.
* If the branches of one Start Parallel node end on different End Parallel nodes, or one End Parallel node receives branches from two different Start Parallel nodes, the run fails with `Missing end parallel gateway` and the cause `Parallel branches merge failed: missing or incompatible end parallel node.`
* A Start Parallel node with no outgoing sequence fails the run with `Workflow is missing outgoing sequences.`

### Runtime view

> [accordion] Console logging
>
> * **Start Parallel node**: the Input tab shows the data handed to the branches.
> * **End Parallel node**: the Output tab shows the merged result of all branches.
> * **Path grouping**: the nodes of each branch are grouped by path, so you can follow one branch at a time.

> [accordion] Path timing
>
> * **Path time**: sum of the execution times of the nodes in that branch.
> * **End Parallel time**: the node's own processing time plus the longest path time.
> * **Example**: if branch A takes 2s and branch B takes 5s, the End Parallel node completes after 5s plus its own processing time.

### Use cases

> [card] Multiple API calls
>
> Call several independent APIs at once instead of one after the other.

> [card] Data enrichment
>
> Fetch data from several sources in parallel and combine it afterwards.

> [card] Notification broadcasting
>
> Send notifications through several channels (email, SMS, push) at the same time.

> [card] Document processing
>
> Process different document types or sections in parallel.

### Limitations

* **Same End Parallel node for all branches**: every branch of a Start Parallel node must end on the same End Parallel node, and that node allows one outgoing sequence.
* **Last-write-wins merge**: write to distinct keys per branch and combine them in a Script node afterwards.
* **No element-level array merge**: you cannot process the first element of an array in one branch and the second in another.
* **No cross-branch reads**: branches run on independent data copies and cannot see each other's writes until the merge.
* **Nesting is allowed**: a branch can contain its own Start Parallel and End Parallel pair.

***

## Script node

Executes custom JavaScript or Python code to transform, map, or enrich data between nodes.

The node's `output` is merged into the workflow data, the same way business rule output is merged into process data: keys the script doesn't write keep their values, and writing `{}` to an existing object changes nothing. To clear a key, set it to `null`. For the full merge rules, see [How output is merged into process data](https://docs.flowx.ai/5.9/docs/building-blocks/supported-scripts.md#how-output-is-merged-into-process-data).

### Calling reusable functions

SaaS · 5.11

> [!NOTE]
>
> **Available on SaaS with FlowX.AI 5.11.** This feature is live on managed (SaaS) deployments now. Self-hosted deployments will receive it with the next LTS release family.

A Script node can call the [reusable functions](https://docs.flowx.ai/5.9/docs/building-blocks/reusable-resources/reusable-functions.md) defined in your project, the same way a process Business Rule action does. Write the call directly in the node's code using the standard syntax:

```javascript theme={"dark"}
functions.projectName.functionName(input.params)
```

The Designer reads the function calls from your script and links the node to those functions automatically, so there is no separate step to register a reference. The linked functions also appear on each function's **Usages** tab, and removing the node clears those usage entries.&#x20;

Function calls resolve from the app version that the workflow runs in, and a function that calls another function resolves its full call chain. Functions defined in a different app are matched by their app name, so cross-app calls work the same way they do in processes.

> [!WARNING]
>
> Reusable functions resolve only in a build context. The Script node's **Test** modal resolves them when you run it against a selected build; a draft or preview run with no build in context resolves no functions, so calls to `functions.*` return no result until a build is selected.

### Testing a script in isolation

A Script node ships with a **Test modal** that lets you exercise the script against sample input without running the full workflow. Open it with the **expand** icon at the top-right corner of the code editor, next to the expand/collapse chevron.

The modal has three side-by-side panels:

| Panel | Purpose |
| - | - |
| **Input** | JSON input passed to the script. Load from a past individual node run or from a past workflow run via the input selector. |
| **Code** | The script body. Inline warnings are surfaced as you type. Includes **Save** and **Test** buttons. |
| **Output** | The JSON returned by the script after running **Test**. Runtime errors are shown here. |

> [!TIP]
>
> The **Test** button runs the current contents of the Code panel, even if you have not clicked **Save** yet. Use this to iterate on a fix without committing changes to the Script node.

> [!NOTE]
>
> Runs triggered from the Test modal are excluded from the Console log History panel, because they may be running unsaved code.

### Run instance IDs in the Logs tab

The Logs tab of a workflow run displays the **node run instance ID** alongside the workflow instance ID for each Script node execution. A copy button is rendered next to every instance ID. Use this to grab a node-run identifier and paste it into the Test modal's input selector to replay the exact past run.

***

## Subworkflow node

The Subworkflow node allows you to modularize complex workflows by invoking other workflows as reusable subcomponents. This approach simplifies process design, promotes reuse, and simplifies maintenance.

**1. Add a Subworkflow Node**

Select **Start Subworkflow** from the **Select Next Node** dropdown. Choose from workflows categorized as **Local** or **Libraries**.

**2. Configure the Subworkflow Node**

* **Workflow Selection**: Pick the workflow to invoke.
* **Open**: Edit the subworkflow in a new tab.
* **Preview**: View the workflow canvas in a popup.
* **Response Key**: Set a key (e.g., `response_key`) for output.
* **Input**: Provide input in JSON format.
* **Output**: Output is read-only JSON after execution.

### Data mapping

SaaS · 5.11

> [!NOTE]
>
> **Available on SaaS with FlowX.AI 5.11.** This feature is live on managed (SaaS) deployments now. Self-hosted deployments will receive it with the next LTS release family.

By default, a Subworkflow node forwards the whole parent context to the subworkflow and nests the result under the response key. **Data mapping** lets you instead pass only selected fields into the subworkflow and land its results directly into named parent variables, using visual input and output mappers rather than raw JSON.

In the node config panel, expand **Data mapping** and turn **Enable data mapping** on.

![Subworkflow node config with Enable data mapping turned on, showing the Start input mapping row and an End Flow output mapping row](https://s3.eu-west-1.amazonaws.com/docx.flowx.ai/5.9/subworkflow-data-mapping-toggle.png)

> [!NOTE]
>
> When data mapping is off, the node keeps its original behavior: the **Input** and read-only **Output** JSON fields described above. Turning **Enable data mapping** on replaces those with the mappers below.

When data mapping is on, the node exposes two mappers, driven by the subworkflow's own start and end nodes:

| Mapper | Helper text | Defined per | How it is labeled |
| - | - | - | - |
| **Input mapping** | Variables sent by the main workflow | The subworkflow start node | A single row labeled **Start** |
| **Output mapping** | Variables received by the main workflow | Each subworkflow end node | One row per end node, labeled with the end node's name |

![Input mapping and Output mapping sections on the Subworkflow node, with the input mapping helper tooltip open](https://s3.eu-west-1.amazonaws.com/docx.flowx.ai/5.9/subworkflow-input-output-mapping-rows.png)

> [!NOTE]
>
> Output mapping applies only to synchronous subworkflows. If the subworkflow runs asynchronously, the parent does not wait for a result, so the **Output mapping** block is hidden.

Each row opens a mapper modal. Use it to draw field-to-field mappings between the data models, then check the result before saving.

**1. Open the mapper**

Click the edit icon on the **Start** row (input) or on an end-node row (output).

**2. Map fields between the two data models**

The modal shows the source data model on the left and the target data model in the middle. Each side is color-coded: **Project Data Model** and **Workflow Data Model**. Draw the field-to-field mappings, or define static values and expression scripts where a direct field match does not exist.

**3. Test the mapping**

Click **Test** to run the current mappings and preview the produced payload in the read-only results panel on the right. Any mapping errors are highlighted so you can correct them before saving.

**4. Apply values from a past run (optional)**

To test against real data, pick a previous workflow instance from **Past Instances Variables** and click **Apply** to load that instance's values as the source example. Use the reset control to return to the default example values.

**5. Save**

Click **Save** to store the mapping on the node. In committed or upper environments the modal is read-only and shows **Close** instead.

![Input mapping modal with the parent workflow variables on the left, mapped subworkflow fields in the middle, and the Test preview payload on the right](https://s3.eu-west-1.amazonaws.com/docx.flowx.ai/5.9/subworkflow-data-mapping-modal.png)

> [!TIP]
>
> Data mapping keeps the subworkflow's contract explicit: the parent passes exactly the fields the subworkflow needs, and only the mapped results flow back. This is easier to maintain than forwarding the entire context and reading everything back through the response key.

**Import, export, and clone**

Subworkflow mappings are stored as part of the workflow definition, so they travel with the node when the workflow is exported, imported, or cloned.

> [!WARNING]
>
> On upgrade, existing Subworkflow nodes keep data mapping turned **off**, so they continue to behave exactly as before. Turn **Enable data mapping** on per node to adopt the mappers.

> [!TIP]
>
> Use subworkflows for reusable logic such as data enrichment, validation, or external system calls.

### Parallel multi-instance

SaaS · 5.12

> [!NOTE]
>
> **Available on SaaS with FlowX.AI 5.12.** This feature is live on managed (SaaS) deployments now. Self-hosted deployments will receive it with the next LTS release family.

A Subworkflow node normally runs the child workflow once. Turn on **Parallel Multi-Instance** to run it once for every element of an array instead, in parallel, and collect the results back into a second array.

This keeps fan-out inside the Integration Designer. Previously, running a workflow once per item meant wrapping it in a process and fanning out with a multi-instance Call Activity.

In the node config panel, expand **Execution** and turn **Parallel Multi-Instance** on.

![Execution section on the Subworkflow node with Parallel Multi-Instance turned on and Enable Data Mapping locked on, showing the required Input Array and Output Array selectors](https://s3.eu-west-1.amazonaws.com/docx.flowx.ai/5.9/subworkflow-execution-parallel-multi-instance.png)

> [!NOTE]
>
> Turning **Parallel Multi-Instance** on also turns **Enable Data Mapping** on and locks it there, because each array element has to be mapped into the child workflow individually. The [data mapping](#data-mapping) mappers stay available and work the same way.

Then set the two arrays:

| Setting | Where | Required | What it does |
| - | - | - | - |
| **Input Array** | Input Mapping | Yes | The array of objects to iterate. One subworkflow instance starts per element. |
| **Output Array** | Output Mapping | Yes | The array each instance's result is collected into. |
| **Correlation attribute** | Output Mapping | No | Decides which output array element each result updates. |

Only array attributes from the workflow data model can be selected, and both arrays must be arrays of objects. If a dropdown offers nothing, the workflow data model has no array attributes to choose from.

![Subworkflow node with the Input Array, Output Array, and Correlation attribute selectors filled with array attributes from the workflow data model](https://s3.eu-west-1.amazonaws.com/docx.flowx.ai/5.9/subworkflow-input-output-array-selectors.png)

**How results land**

The correlation attribute behaves as an upsert key rather than a strict join:

* A result whose correlation value matches an existing element of the output array updates that element.
* A result that matches no element is appended.
* With no correlation attribute set, every result is appended.

Correlation candidates are the primitive fields of the output array's element, so pick the output array first. Until you do, the **Correlation attribute** selector stays empty.

If the merge fails, the still-running instances are cancelled and the output array is emptied. See [Routing a subworkflow failure](#routing-a-subworkflow-failure).

> [!TIP]
>
> Point the input and output arrays at different data model attributes. Reusing one array as both the source and the target makes the upsert behavior hard to reason about.
>
> Each instance runs the same subworkflow against one element, so if you need per-element branching, put it inside the child workflow rather than around the Subworkflow node.

> [!NOTE]
>
> **Parallel multi-instance is not the same as [parallel branches](#start-parallel-and-end-parallel-nodes).** Start Parallel and End Parallel nodes run several *different* branches of one workflow at the same time. Parallel multi-instance runs *one* subworkflow many times, once per element of an array. Use parallel branches for different work in parallel, and multi-instance for the same work over a collection.

### Execution logic and error handling

* Parent workflow waits for subworkflow completion before proceeding.
* If the subworkflow fails, the parent workflow halts at this node. On SaaS (5.12), connecting the node's failure exit routes the failure down its own branch instead. See [Routing a subworkflow failure](#routing-a-subworkflow-failure).
* Subworkflow output is available to downstream nodes via the response key.
* Logs include workflow name, instance ID, and node statuses for both parent and subworkflow.

> [!WARNING]
>
> If a subworkflow is deleted, an error displays: `[name] subworkflow not found.`

> [!TIP]
>
> Subworkflow runs are recorded in workflow instance history for traceability.

### Routing a subworkflow failure

SaaS · 5.12

> [!NOTE]
>
> **Available on SaaS with FlowX.AI 5.12.** This feature is live on managed (SaaS) deployments now. Self-hosted deployments will receive it with the next LTS release family.

A Subworkflow node has two exits, drawn as a green connector for success and a red one for failure. Connect the red exit to send a failed run down its own branch instead of stopping the parent workflow.

This is opt-in per node. A node whose red exit is not connected fails and blocks exactly as it did before, and nothing is changed for existing workflows.

Only a **mapping** failure is routed, because that is the kind of failure a workflow can be built to handle: the same workflow succeeds for records that carry the right fields.

| Routed down the failure exit | Still stops the workflow |
| - | - |
| The input mapper fails | No subworkflow is set on the node |
| The input it produced does not satisfy what the subworkflow expects | The response key is missing |
| The output mapper fails when merging the result back | The node has no mapping configured at all |
| | The run hits the maximum node or subworkflow-depth limit |

On the failure branch, the node writes a fixed **`subWorkflowError`** key, separate from its response key:

| Field | Description |
| - | - |
| `hasError` | Always `true`. It exists so a branch reads the same shape here as on other node types. |
| `errorMessage` | For example, `The input of subworkflow node [name] could not be mapped, so no subworkflow was started.` |
| `errorType` | `INPUT_MAPPING` or `OUTPUT_MAPPING`, so a branch can tell a child that never started from one that ran and could not be merged back. |
| `childInstanceUuid` | The run whose result could not be mapped back. Left out entirely when no child was started. |

> [!NOTE]
>
> The failure exit covers the node's own mapping. A subworkflow that fails **inside itself** still cascades to the parent, whether or not the failure exit is connected.
>
> A manual single-node run also reports the error to you rather than following the branch.

> [!WARNING]
>
> On a [parallel multi-instance](#parallel-multi-instance) node, a merge failure cancels the sibling instances that are still running and empties the output array before taking the failure branch, so the branch never reads a half-filled list.

### Console logging, navigation, and read-only mode

* Console shows input/output, workflow name, and instance ID for each subworkflow run.
* Open subworkflow in a new tab for debugging from the console.
* Breadcrumbs enable navigation between parent and subworkflow details.
* In committed/upper environments, subworkflow configuration is read-only and node runs are disabled (preview/open only).

> [!NOTE]
>
> Subworkflow instances are logged in history, and you can navigate between parent and child workflow runs for comprehensive debugging.

### Use case: CRM Data Retrieval with subworkflows

Suppose you need to retrieve CRM details in a subworkflow and use the output for further actions in the parent workflow.

**1. Create the Subworkflow**

Design a workflow that connects to your CRM system, fetches user details, and outputs the data in a structured JSON format.

**2. Add a Subworkflow Node in the Parent Workflow**

In your main workflow, add a Subworkflow Node and select the CRM retrieval workflow. Map any required input parameters.

**3. Use Subworkflow Output in Parent Workflow**

Downstream nodes in the parent workflow can reference the subworkflow's output using the defined `responseKey`.

```json theme={"dark"}
{
  "crmData": "${responseKey}"
}
```

**4. Monitor and Debug**

Use the console to view input/output data, workflow names, and instance IDs. Open subworkflow runs in new tabs for detailed debugging.

> [!NOTE]
>
> This modular approach allows you to build scalable, maintainable integrations by composing workflows from reusable building blocks.

***

## End node

The End node signifies the termination of a workflow's execution. It collects the final output and completes the workflow process.

* Receives input in JSON format from the previous node.
* Output represents the final data model of the workflow.
* Multiple End nodes are allowed for different execution paths.

> [!NOTE]
>
> If the node's output doesn't meet mandatory requirements, it will be flagged as an error to ensure all necessary data is included. Output parameters declared in the workflow's output schema are a runtime contract: when any of them resolves to no value, the run fails with `The mandatory output parameters of the <workflow> workflow are missing at runtime` - see the [error glossary](https://docs.flowx.ai/5.9/docs/resources/error-glossary.md#the-mandatory-output-parameters-of-the-workflow-are-missing-at-runtime).

> [!WARNING]
>
> A placeholder that doesn't resolve fails **silently** on an End node: the output field is set to `null`, and the only error you see is the mandatory-output failure above. When mapping an End node output:
>
> * Use the bare key with no prefix: `${responseKey.data}`, not `${output.value}` or `${input.responseKey.data}`.
> * Check the spelling against the Response Key configured on the REST node - the response body lives under `<responseKey>.data`.
> * Leave the placeholder unquoted for numeric or boolean values; quote it (`"${key}"`) only for strings.
>
> To confirm what an End node actually produced, open the node in the workflow run details: it shows the exact input it received and the output JSON it emitted.


## Related topics

- [Workflow node reference: data, document, and AI nodes](https://docs.flowx.ai/5.9/docs/platform-deep-dive/integrations/workflow-nodes-data-and-ai.md)
- [Create and run workflows in Integration Designer](https://docs.flowx.ai/5.9/docs/platform-deep-dive/integrations/integration-workflows.md)
- [Adding an action to a node](https://docs.flowx.ai/5.9/docs/flowx-designer/managing-a-project-flow/adding-an-action-to-a-node.md)
- [Integration Designer](https://docs.flowx.ai/5.9/docs/platform-deep-dive/integrations/integration-designer.md)
- [Reference types in resources usage](https://docs.flowx.ai/5.9/docs/projects/reference-types.md)


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