Call Flows from Other Flows and Build Reusable Components

For the complete documentation index, see llms.txt. For a full content snapshot, see llms-full.txt. Append .md to any kestra.io/docs/* URL for plain Markdown.

Subflows let you build modular and reusable workflow components. Executing a subflow creates a new flow execution from within another flow.

Declaring a subflow

For example, you might define a subflow that handles error alerts by posting to Slack and email. Any flow that needs error notifications calls this subflow rather than duplicating those tasks.

How to declare a subflow

Use the io.kestra.plugin.core.flow.Subflow task and specify the flowId and namespace of the flow to execute. Pass custom inputs the same way you would pass arguments to a function.

The wait and transmitFailed properties control execution behavior. If wait is false, the parent flow continues without waiting for the subflow to finish. transmitFailed determines whether a failure in the subflow causes the parent flow to fail.

Practical example

id: critical_service
namespace: company.team
tasks:
- id: return_data
type: io.kestra.plugin.jdbc.duckdb.Query
sql: |
INSTALL httpfs;
LOAD httpfs;
SELECT sum(total) as total, avg(quantity) as avg_quantity
FROM read_csv_auto('https://huggingface.co/datasets/kestra/datasets/raw/main/csv/orders.csv', header=True);
store: true
outputs:
- id: some_output
type: STRING
value: "{{ outputs.return_data.uri }}"
id: parent_service
namespace: company.team
tasks:
- id: subflow_call
type: io.kestra.plugin.core.flow.Subflow
namespace: company.team
flowId: critical_service
wait: true
transmitFailed: true
- id: log_subflow_output
type: io.kestra.plugin.scripts.shell.Commands
taskRunner:
type: io.kestra.plugin.core.runner.Process
commands:
- cat "{{ outputs.subflow_call.outputs.some_output }}"

Subflow properties

io.kestra.plugin.core.flow.Subflow task properties:

FieldDescription
flowIdThe subflow’s identifier
namespaceThe namespace where the subflow is located
inheritLabelsDetermines if the subflow inherits labels from the parent (default: false).
inputsInputs passed to the subflow
labelsLabels assigned to the subflow
outputs (deprecated)Passes subflow outputs to the parent flow. Use flow outputs instead.
revisionThe subflow revision to execute (defaults to the latest)
scheduleDateSchedule subflow execution on a specific date rather than immediately.
transmitFailedIf true, parent flow fails when the subflow execution fails or is killed (requires wait to be true; default: true). A cancelled subflow execution ends the Subflow task CANCELLED instead, and the parent continues.
waitIf true, parent flow waits for subflow completion (default: true).

Killing parent and subflow executions

When a parent execution waits on a subflow, the final state of each execution depends on which one you kill and how.

Killing the parent execution

Clicking Kill on a parent execution kills the parent and its running subflow executions. The dropdown next to Kill offers both options explicitly:

  • Kill current and subflows kills the parent and its running subflow executions. The parent, the Subflow task run, and the subflow execution all end KILLED.
  • Kill current only kills the parent and lets the subflow execution finish. The parent stays KILLING until the subflow execution ends, then ends KILLED. No further tasks run in the parent.

Through the API, DELETE /api/v1/{tenant}/executions/{executionId}/actions/kill kills subflow executions by default. Add ?isOnKillCascade=false to kill the current execution only. The bulk kill endpoints, DELETE /api/v1/{tenant}/executions/kill/by-ids and DELETE /api/v1/{tenant}/executions/kill/by-query, never kill subflow executions.

Killing the subflow execution

When you kill a subflow execution directly, the parent treats it as a failure of the Subflow task, governed by transmitFailed and allowFailure:

Subflow task configurationSubflow task runParent execution
transmitFailed: trueKILLEDSkips the remaining tasks, runs any errors tasks, and ends FAILED.
transmitFailed: true and allowFailure: trueWARNINGContinues with the next tasks and ends WARNING.
transmitFailed: falseSUCCESSContinues with the next tasks.

A retry on the Subflow task does not restart a killed subflow execution.

Cancelled subflow executions

A subflow execution ends CANCELLED when the system cancels it, for example when the subflow’s concurrency limit is reached with behavior: CANCEL. Unlike a failed or killed subflow execution, a cancelled one does not stop the parent: the parent runs its next tasks either way. transmitFailed and allowFailure decide how the Subflow task run and the parent end:

Subflow task configurationSubflow task runParent execution
transmitFailed: trueCANCELLEDContinues with the next tasks, does not run errors tasks, and ends CANCELLED.
transmitFailed: true and allowFailure: trueWARNINGContinues with the next tasks and ends WARNING.
transmitFailed: true, allowFailure: true, and allowWarning: trueSUCCESSContinues with the next tasks.
transmitFailed: falseSUCCESSContinues with the next tasks.

When the Subflow task run ends CANCELLED, its logs include a warning: Subflow execution ... ends in CANCELLED state.

Passing data between parent and child flows

Any flow can emit outputs accessible to the parent flow. For more details and examples, see the Outputs page.

Accessing outputs from a subflow execution

Outputs include the execution ID, extracted outputs, and the final state (if wait is true). For example:

id: flow_outputs
namespace: company.team
tasks:
- id: mytask
type: io.kestra.plugin.core.output.OutputValues
values:
result: this is a task output used as a final flow output
outputs:
- id: final
type: STRING
value: "{{ outputs.mytask.values.result }}"

Access these outputs in the parent flow:

id: parent_flow
namespace: company.team
tasks:
- id: subflow
type: io.kestra.plugin.core.flow.Subflow
flowId: flow_outputs
namespace: company.team
wait: true
- id: log_subflow_output
type: io.kestra.plugin.core.log.Log
message: "{{ outputs.subflow.outputs.final }}"

Passing inputs to a subflow

The example below passes a URL input from the parent flow into the subflow.

Subflow:

id: subflow_example
namespace: company.team
inputs:
- id: http_uri
type: STRING
tasks:
- id: download
type: io.kestra.plugin.core.http.Request
uri: "{{ inputs.http_uri }}"
- id: log
type: io.kestra.plugin.core.log.Log
message: "{{ outputs.download.body }}"
outputs:
- id: data
type: STRING
value: "{{ outputs.download.body }}"

Parent flow:

id: inputs_subflow
namespace: company.team
inputs:
- id: url
type: STRING
tasks:
- id: subflow
type: io.kestra.plugin.core.flow.Subflow
flowId: subflow_example
namespace: company.team
inputs:
http_uri: "{{ inputs.url }}"
wait: true
- id: hello
type: io.kestra.plugin.core.log.Log
message: "{{ outputs.subflow.outputs.data }}"

Nested inputs

In the example below, the flow extracts JSON data from a REST API and passes it to a subflow as a nested input:

id: extract_json
namespace: company.team
tasks:
- id: api
type: io.kestra.plugin.core.http.Request
uri: https://dummyjson.com/users
- id: read_json
type: io.kestra.plugin.core.log.Log
message: "{{ outputs.api.body }}"
- id: subflow
type: io.kestra.plugin.core.flow.Subflow
namespace: company.team
flowId: subflow
inputs:
users.firstName: "{{ outputs.api.body | jq('.users') | first | first | jq('.firstName') | first }}"
users.lastName: "{{ outputs.api.body | jq('.users') | first | first | jq('.lastName') | first }}"
wait: true
transmitFailed: true

Nested inputs in the subflow definition add type validation to extracted JSON fields:

id: subflow
namespace: company.team
inputs:
- id: users.firstName
type: STRING
defaults: Rick
- id: users.lastName
type: STRING
defaults: Astley
tasks:
- id: process_user_data
type: io.kestra.plugin.core.log.Log
message: hello {{ inputs.users }}

You can then pass the entire users object, including nested fields, to any task in the subflow.

Was this page helpful?