Skip to main content

CallFlow Node

CallFlow Node

The CallFlow node invokes another flow as a sub-flow, passing data and receiving results. It enables modular workflow design, allowing you to reuse common logic across multiple workflows.

How It Works

The CallFlow node:

  1. Takes input data from the current flow
  2. Passes parameters to the target sub-flow
  3. Executes the sub-flow (which has a FromFlow trigger)
  4. Receives the response data
  5. Continues processing in the parent flow

Adding a CallFlow

  1. Drag CallFlow from the Inputs section of the Element Panel
  2. Connect it to your data flow
  3. Click the CallFlow node to configure
  4. Select the target flow and map parameters

Configuration Panel

Target Flow Selection

Select Flow: Choose which flow to invoke. Only flows with FromFlow triggers are available as targets.

Requirements for target flow:

  • Must have a FromFlow trigger (not HTTP or Schedule)
  • Must be saved (not necessarily deployed)
  • Its FromFlow trigger declares the input parameters this node can pass — a flow that declares none is callable, it just takes no input

Input Mode

Choose how data is passed to the sub-flow:

Single Row Mode

Calls the sub-flow once for each row:

How it works:

  • Each input row triggers one sub-flow execution
  • Row fields are available as parameters
  • Sub-flow runs N times for N input rows

Use when:

  • Processing individual records
  • Each item needs separate processing
  • Results should map back to input rows

Example:

10 orders → CallFlow → 10 sub-flow executions → 10 results

Batch Mode

Calls the sub-flow once with all data:

How it works:

  • All input rows passed as a single parameter
  • Sub-flow runs once
  • Returns aggregated results

Use when:

  • Bulk processing
  • Aggregated operations
  • Performance optimization

Example:

10 orders → CallFlow → 1 sub-flow execution → bulk results

Parameter Mapping

The called flow owns the call contract. It declares its input parameters — their names, their types, and whether each is required — on its FromFlow trigger. The CallFlow node does not re-declare them. It reads them, shows each one read-only, and asks only one question per parameter: which upstream field feeds it?

Configuration:

Sub-Flow ParameterDeclared by the sub-flowSource field (you choose)
customerIdtext, requiredCustomerID
orderDatedatetimeOrderDate
tagslist(text)Labels

This is why there is one declaration and not two. Previously the caller re-declared each parameter's type, nothing checked it against the called flow, and the publisher built the call from the caller's declaration — so a sub-flow that declared tags as a list of text could still be called with a list of objects. The declaration was decorative. Now the publisher resolves every binding from the called flow's declaration, and:

  • Publishing fails if a mapping names a parameter the called flow does not declare. The error names the parameters it does declare.
  • The source-field picker only offers compatible fields. A list(number) parameter will not offer a list-of-text field.
  • Changing the sub-flow's parameters marks its callers stale, so you find out at author time rather than at runtime.

To add or retype a parameter, edit the sub-flow's FromFlow trigger — that is the only place it is declared.

Dynamic Call Flow — the unenforced exception

A Dynamic Call Flow node picks its target flow at runtime, from a field value in the data (the flow name field). Everything above assumes the called flow is known while you are authoring — and for a Dynamic Call Flow it is not.

So the contract cannot be enforced there, and it isn't:

CallFlow (static)Dynamic Call Flow
Target flowChosen at design timeResolved at runtime from a field
Parameter namesRead from the called flow, read-onlyFree-form — you type them
Parameter typesDeclared by the called flowNot declared anywhere
Unknown parameterPublishing failsNot checked
Source-field pickerFiltered by the declared typeUnfiltered

This asymmetry is deliberate, not an oversight. A Dynamic Call Flow is the designated escape hatch for "the target isn't knowable yet" — and since the target isn't knowable, nothing downstream of it can be checked either. If you want the parameter names and types checked, use a static CallFlow.

A misspelled parameter name on a Dynamic Call Flow is silently passed through and the sub-flow simply never receives it. Verify the names against the target flow's FromFlow trigger by hand.

Response Handling

Configure how sub-flow results are integrated:

Structured Response Format

CallFlow returns a structured response with status and results:

Success Response:

{
"status": true,
"response": [
{ "field1": "value1", "field2": "value2" },
{ "field1": "value3", "field2": "value4" }
]
}

Failure Response:

{
"status": false,
"response": "Flow 'ProcessOrder' returned no data"
}

Output Fields:

FieldTypeDescription
statusBooleantrue if sub-flow executed successfully, false otherwise
responseArray or StringArray of result rows on success, error message on failure

Tip: Use a Condition node after CallFlow to check ${status} before processing ${response} data. This ensures robust error handling in your parent flow.

Include Response Fields

Select which fields from the sub-flow response to include:

All fields: Sub-flow output fields are added to the result.

Selected fields: Choose specific fields to include.

Field Naming

Prevent field name conflicts:

  • Prefix - Add prefix like subflow_
  • Rename - Map individual field names
  • Replace - Sub-flow fields replace input fields

Synchronous vs Asynchronous

Synchronous Execution (Default)

Flow waits for sub-flow to complete:

Parent: [Node A] → [CallFlow] → [Node B]

Waits for result

Continues to B

Use when:

  • You need the sub-flow result to continue
  • Subsequent processing depends on output
  • Real-time response required

Asynchronous Execution

Flow continues without waiting:

Parent: [Node A] → [CallFlow Async] → [Node B]

Queues sub-flow

Immediately continues to B

Use when:

  • Sub-flow is fire-and-forget
  • No result needed
  • Performance is priority

Note: Async mode doesn't return data to parent flow.

Common Use Cases

Data Enrichment

Parent flow: Customer orders Sub-flow: Get customer credit rating

[Orders] → [CallFlow: GetCreditRating] → [Continue with enriched orders]
Params: customerId = {CustomerID}
Returns: creditScore, creditLimit, rating

Validation Logic

Parent flow: Process transactions Sub-flow: Validate transaction

[Transactions] → [CallFlow: ValidateTransaction] → [Condition: Valid?]
Params: amount, type, account
Returns: isValid, validationMessages

Calculation Services

Parent flow: Invoice processing Sub-flow: Calculate tax

[InvoiceLines] → [CallFlow: CalculateTax] → [Continue]
Params: amount, state, productType
Returns: taxRate, taxAmount, totalWithTax

Complex Transformations

Parent flow: Reporting Sub-flow: Aggregate metrics

[Raw Data] → [CallFlow: ComputeMetrics (batch)] → [Report Output]
Params: all data as array
Returns: aggregated metrics

External System Integration

Parent flow: Order processing Sub-flow: Check inventory (encapsulates external API)

[Orders] → [CallFlow: CheckInventory] → [Continue with availability]
Params: sku, quantity
Returns: available, warehouse, estimatedDate

Designing Sub-Flows

Sub-Flow Structure

A well-designed sub-flow has:

  1. FromFlow Trigger - Defines input parameters
  2. Processing Logic - Transforms, lookups, calculations
  3. Response Node - Returns output data
[FromFlow: params] → [Processing] → [Response]

Input Parameters

Define clear, typed parameters:

Example sub-flow inputs:

ParameterTypeRequiredDefault
customerIdStringYes-
includeHistoryBooleanNofalse
maxRecordsNumberNo100

Output Schema

Return consistent, well-defined output:

Example sub-flow outputs:

FieldTypeDescription
successBooleanOperation result
dataObjectRequested data
errorMessageStringError if failed

Error Handling

Sub-flows should handle errors gracefully:

  1. Catch potential errors
  2. Return error information in response
  3. Let parent flow decide how to proceed
Response: { success: false, errorCode: "NOT_FOUND", message: "Customer not found" }

Row-by-Row Processing

For single row mode, each input row is processed:

Parent flow data:

OrderIDCustomerIDAmount
O001C100500
O002C101750
O003C100200

CallFlow executes 3 times:

  1. With CustomerID=C100
  2. With CustomerID=C101
  3. With CustomerID=C100

Results merged back:

OrderIDCustomerIDAmountCreditScoreRating
O001C100500750A
O002C101750680B
O003C100200750A

Batch Processing

For batch mode, all data passed at once:

Parent flow: 100 line items

CallFlow (batch mode):

  • Sub-flow receives array of 100 items
  • Processes all items together
  • Returns aggregate or individual results

Performance benefit: 1 call instead of 100 calls

Nesting Depth

CallFlow nodes can be nested:

Flow A → CallFlow B → CallFlow C → CallFlow D

Considerations:

  • Each level adds latency
  • Debugging becomes harder
  • Keep nesting shallow (2-3 levels max)

Best practices:

  • Flatten when possible
  • Use batch mode to reduce calls
  • Monitor execution time

Error Handling

Sub-Flow Errors

When a sub-flow fails:

Default behavior:

  • Parent flow fails if sub-flow fails
  • Error propagates up

Error response handling:

  • Sub-flow returns error in response
  • Parent checks and handles
  • Parent continues or branches

Example:

[CallFlow] → [Condition: success = true?]
├── True → [Continue processing]
└── False → [Error handling]

Timeout Handling

Configure sub-flow timeouts:

Timeout options:

  • Default timeout (from system settings)
  • Custom timeout per CallFlow

On timeout:

  • Sub-flow execution cancelled
  • Error returned to parent
  • Handle in parent flow

Performance Optimization

Minimize Calls

Before: 1000 calls for 1000 rows

[1000 rows] → [CallFlow: Single Row Mode]

After: 1 call for 1000 rows

[1000 rows] → [CallFlow: Batch Mode]

Cache When Possible

If sub-flow returns same result for same input:

  • Consider caching at sub-flow level
  • Or pre-fetch and use Lookup instead of CallFlow

Parallel Execution

Multiple CallFlow nodes can run in parallel:

[Data] → [CallFlow A] ─┐
├─ [Merge/Append]
→ [CallFlow B] ─┘

Both sub-flows execute simultaneously.

Troubleshooting

Sub-Flow Not Found

Possible causes:

  • Target flow deleted
  • Flow name changed
  • Wrong workspace

Solutions:

  • Verify flow exists
  • Reselect target flow
  • Check workspace context

Parameter Mismatch

"Flow X does not declare an input parameter named Y" on publish.

The mapping names a parameter the called flow does not declare — usually because the sub-flow's FromFlow trigger renamed or removed it after the caller was authored. The error lists the parameters the sub-flow does declare.

Solutions:

  • Declare the parameter on the sub-flow's FromFlow trigger, or
  • Reopen the CallFlow node and re-map to a parameter it does declare. The panel flags mappings the callee no longer declares and drops them when you save.

No compatible source field offered.

The picker only offers fields whose type matches the parameter's declared type — a list(number) parameter accepts a list of numbers, not a list of text, and not a bare number. Either change the parameter's declared type on the sub-flow, or transform the field upstream (with a Map node) into the type the sub-flow asks for. Types are never silently coerced at runtime.

Empty Results

Possible causes:

  • Sub-flow not returning data
  • Response node not configured
  • Data filtered out in sub-flow

Solutions:

  • Test sub-flow independently
  • Check Response node configuration
  • Preview sub-flow output

Performance Issues

Symptoms: Slow execution with many calls

Solutions:

  • Switch to batch mode
  • Cache repeated lookups
  • Optimize sub-flow logic
  • Consider denormalizing

Examples

Customer Enrichment Sub-Flow

Parent: Order Processing

[Orders] → [CallFlow: EnrichCustomer] → [Continue]
Params: customerId = {CustomerID}

Sub-Flow: EnrichCustomer

[FromFlow: customerId]
→ [Entity: Customers WHERE ID = customerId]
→ [Lookup: CustomerSegments]
→ [Transform: add calculated fields]
→ [Response: customer details]

Tax Calculation Service

Parent: Invoice Creation

[LineItems] → [CallFlow: CalculateTax (batch)] → [Append tax to lines]
Params: items = all rows
Returns: items with tax amounts

Sub-Flow: CalculateTax

[FromFlow: items array]
→ [Lookup: TaxRates by state and productType]
→ [Transform: calculate tax per item]
→ [Response: items with taxAmount, taxRate]

Inventory Check

Parent: Order Fulfillment

[OrderLines] → [CallFlow: CheckInventory] → [Condition: In Stock?]
Params: sku, quantity
Returns: available, warehouse

Sub-Flow: CheckInventory

[FromFlow: sku, quantity]
→ [Entity: Inventory WHERE SKU = sku]
→ [Transform: available = QuantityOnHand >= quantity]
→ [Response: available, warehouse, quantityOnHand]

Next Steps