diff --git a/platform/deploy-operate/upgrade.md b/platform/deploy-operate/upgrade.md index c401c35..ae411e2 100644 --- a/platform/deploy-operate/upgrade.md +++ b/platform/deploy-operate/upgrade.md @@ -567,6 +567,46 @@ No migration. `[governance.opa] enabled` stays `false` by default. See [Trino on - **Behaviour:** an OPA request whose body does not arrive within `body_read_timeout_ms` is answered 408 and counted in the new `egret_opa_unread_total`. Restrict `/opa/` to the engines' network: it is now required. +### Workflow outputs: Loop outputs, Loop retry and flow outputs (platform #840) + +One migration adds columns for execution and task-run outputs; it only adds columns and applies at boot. There is no backfill: an +execution that ended before the upgrade has no outputs. **Behaviour change for stored flows:** a flow saved earlier can give a +different answer, because each change makes egret do what Kestra does. See [Flow control](../workflows/flows/flow-control.md#loop-outputs) +and [Flow outputs](../workflows/flows/inputs-and-outputs.md#flow-outputs). + +| Flow | Before | Now | +|---|---|---| +| A `Loop` with `outputs:` | Accepted, never evaluated; `{{ outputs.loop.outputs }}` failed to render | Evaluated at the end of each iteration and collected in `outputs..outputs` | +| Any `Loop` run | No counters | `iterationCount`, `runningIterations` and `terminatedIterations` | +| A `Loop` output that fails to render or convert | Ignored | The iteration gets `FAILED`; with `transmitFailed: true` the `Loop` fails | +| `fetchType: STORE` (or `AUTO` over a `kestra://`, `file://` or `nsfile://` `values:`) | Ignored | One `outputs.ion` file per iteration; the list holds `{uri}` | +| A `retry:` on a `Loop` or on an ancestor of it | Dropped for the tasks inside each iteration | Inherited by every task and `Subflow` of each iteration | +| A failed flowable task under an ancestor's or the flow's `retry:` | Its parent stayed `RUNNING` forever | Its parent ends `FAILED` (bug fix) | +| A flow-level `outputs:` | Never evaluated | Evaluated when the execution ends; a failure ends it `FAILED` | +| `{{ execution.outputs }}` and a `Subflow` parent's `outputs..outputs` | Always `{}` | The evaluated outputs; a child whose declared outputs were not stored fails the `Subflow` run | +| `{{ trigger.outputs }}` in a Flow trigger's `inputs:` | Failed (`trigger` unknown) | The triggering execution's outputs | +| `GET` of one execution | No outputs | An `outputs` field (`SECRET` values are `******`); the list does not carry it | +| A parent reading its `Subflow` child's `FILE` output with `read()` | Refused | Allowed, for the files the child published | + +Limits that exist in Egret only (Kestra has none): + +- One `Loop` iteration's outputs may serialise to at most 1 MiB and a `Loop`'s whole list to 16 MiB; a flow's outputs to 16 MiB per + execution. Beyond a limit the iteration or execution fails with a message that points to `fetchType: STORE` or to a `FILE` output. +- A `type: FILE` output must name a file of the execution itself or of its `Loop` family; anything else is refused with + `egret refuses output : is not a file of this execution`. A parent cannot re-publish its `Subflow` child's file. +- A `SECRET` output with `fetchType: STORE` is refused, when you save and when it runs. Other `SECRET` outputs are sealed with + `secrets.master_key` and shown as `******`; without a master key the output fails and names the setting. +- Duplicate output ids inside a `Loop`'s `outputs:` are refused when you save (`duplicate_output_id`), and `secret()` is refused in an + output's `value:`. +- A `dependsOn` Flow trigger does not fire on an event whose merged `trigger.outputs` would pass 16 MiB; the log names the sizes + and the window is kept. + +Known differences from Kestra: the parent flow's `outputs:` are not evaluated inside each `Loop` iteration; a STORE file is +named `outputs.ion` and `nextOffset` is not written; a `Subflow` child that ended before it ran and declares only literal outputs +fails the `Subflow` run; a `retry:` with `behavior: CREATE_NEW_EXECUTION` that reaches a task inside a `Loop` is refused when it +runs. A `SECRET` value that an author renders into a `Subflow`'s `inputs:` or a `STRING` flow output is stored in clear and +returned by the API; keep `SECRET` values in `SECRET` outputs and inputs. + ## Rolling back There is no supported in-place rollback. Restore the database from the backup taken in step 2, then start the previous diff --git a/platform/known-issues.md b/platform/known-issues.md index 8f067f4..7ea2996 100644 --- a/platform/known-issues.md +++ b/platform/known-issues.md @@ -189,7 +189,7 @@ Collected while writing the detailed pages for each area (October 2026), checked | KI-114 | workflows | Gap | S2 | `concurrency`, `checks`, `sla`, `quotas` only warn; two runs of a `limit: 1` flow ran together. *Steps:* docs.demo/one-at-a-time. | | KI-115 | workflows | Gap | S2 | `trigger.*` (date, body) unreadable in task expressions; `{{ trigger.date }}` in a scheduled flow fails with "undefined value". *Steps:* nightly-heartbeat first runs. | | KI-116 | workflows | Gap | S2 | API/schedule/webhook starts do not validate required inputs, types or SELECT values; only the run form does. *Steps:* docs.demo/required-inputs executions. | -| KI-117 | workflows | Gap | S2 | Flow `outputs:` are not computed; Subflow `outputs` always `{}`. *Steps:* call-subflow / add-numbers. | +| KI-117 | workflows | Gap | S2 | Flow `outputs:` are not computed; Subflow `outputs` always `{}`. *Steps:* call-subflow / add-numbers. *Status:* fixed in platform #840 (November 2026, workflow package #6): a flow's `outputs:` are evaluated when the execution ends and stored on it, `{{ execution.outputs }}` and a `Subflow` parent's `outputs..outputs` carry them, and `GET /executions/{id}` returns them as `outputs`. | | KI-118 | workflows | Gap | S3 | YAML input defaults are not parsed into an object in expressions. *Steps:* docs.demo/input-types-tour. | | KI-119 | workflows | Bug | S2 | Deleting a flow keeps its namespace/id reserved ("a trashed flow still holds"), with no way to restore or purge. *Steps:* DELETE a flow then POST same id. *Status:* fixed in platform #747 (October 2026): **Flows → Trash** (`GET /flows?trashed=true`) lists the trash. **Restore** (`POST /flows/{id}/restore`) brings a flow back and recreates its schedules from their next tick. **Delete permanently** (`DELETE /flows/{id}/permanent`, type-to-confirm) works only on a flow in the trash: it deletes the flow, its versions and its trigger state, frees its namespace and id, and keeps its executions. Both need `workflow:manage` and are audited. The 409 on a taken name now says *restore it, or delete it permanently to free the name*. | | KI-120 | workflows | Bug | S3 | Served schema's `DependsOn` lists `condition`/`inputs`, but the engine reads namespace/flowId/states/labels/when. *Steps:* GET /workflows/schema. *Status:* closed, does not reproduce (October 2026, checked for platform #747): the served `io.kestra.core.models.flows.DependsOn` (`inputs`, `condition`) is the **input** `dependsOn`, used only by the input definitions, and that is what the engine reads for an input. A Flow trigger's `dependsOn` items point to `Flow-Dependency` (`namespace`, `flowId`, `states`, `labels`, `when`), which is what the engine reads for a trigger. | diff --git a/platform/reference/rest-api/workflow.md b/platform/reference/rest-api/workflow.md index 34c9fa6..657c83c 100644 --- a/platform/reference/rest-api/workflow.md +++ b/platform/reference/rest-api/workflow.md @@ -127,6 +127,7 @@ Operation id: `get_execution`. Auth: Session cookie. Forbidden (403): Missing wo | `state` | string | yes | Kestra's wire spelling — `RUNNING`, `SUCCESS`, `FAILED`, … | | `history` | string[] | yes | The state timeline, as stored: an array of state NAMES, e.g. `["CREATED","RUNNING","SUCCESS"]`. It carries no timestamps — see [`TaskRunDto::history`], which is written by the same function. | | `inputs` | object | yes | | +| `outputs` | object | no | The flow's `outputs:` as evaluated when the execution ended; absent when none were. A `SECRET` value is `******`. Only the single-execution `GET` fills it; the executions list does not carry it. | | `seq` | integer (int64) | yes | | | `execution_trigger` | object, nullable | no | The trigger record, `NULL` for an API start. | | `trigger_execution_id` | string (uuid), nullable | no | | @@ -289,6 +290,7 @@ Tasks retrying, paused or not yet started end `KILLED` at once; a running task i | `state` | string | yes | Kestra's wire spelling — `RUNNING`, `SUCCESS`, `FAILED`, … | | `history` | string[] | yes | The state timeline, as stored: an array of state NAMES, e.g. `["CREATED","RUNNING","SUCCESS"]`. It carries no timestamps — see [`TaskRunDto::history`], which is written by the same function. | | `inputs` | object | yes | | +| `outputs` | object | no | The flow's `outputs:` as evaluated when the execution ended; absent when none were. A `SECRET` value is `******`. Only the single-execution `GET` fills it; the executions list does not carry it. | | `seq` | integer (int64) | yes | | | `execution_trigger` | object, nullable | no | The trigger record, `NULL` for an API start. | | `trigger_execution_id` | string (uuid), nullable | no | | @@ -384,6 +386,7 @@ Run a finished execution again. | `state` | string | yes | Kestra's wire spelling — `RUNNING`, `SUCCESS`, `FAILED`, … | | `history` | string[] | yes | The state timeline, as stored: an array of state NAMES, e.g. `["CREATED","RUNNING","SUCCESS"]`. It carries no timestamps — see [`TaskRunDto::history`], which is written by the same function. | | `inputs` | object | yes | | +| `outputs` | object | no | The flow's `outputs:` as evaluated when the execution ended; absent when none were. A `SECRET` value is `******`. Only the single-execution `GET` fills it; the executions list does not carry it. | | `seq` | integer (int64) | yes | | | `execution_trigger` | object, nullable | no | The trigger record, `NULL` for an API start. | | `trigger_execution_id` | string (uuid), nullable | no | | @@ -442,6 +445,7 @@ W7b Task 3 fix round 1, the user's ruling of 2026-10-11, after review found the | `state` | string | yes | Kestra's wire spelling — `RUNNING`, `SUCCESS`, `FAILED`, … | | `history` | string[] | yes | The state timeline, as stored: an array of state NAMES, e.g. `["CREATED","RUNNING","SUCCESS"]`. It carries no timestamps — see [`TaskRunDto::history`], which is written by the same function. | | `inputs` | object | yes | | +| `outputs` | object | no | The flow's `outputs:` as evaluated when the execution ended; absent when none were. A `SECRET` value is `******`. Only the single-execution `GET` fills it; the executions list does not carry it. | | `seq` | integer (int64) | yes | | | `execution_trigger` | object, nullable | no | The trigger record, `NULL` for an API start. | | `trigger_execution_id` | string (uuid), nullable | no | | @@ -759,6 +763,7 @@ Operation id: `start_flow_execution`. Auth: Session cookie and `X-Egret-CSRF` he | `state` | string | yes | Kestra's wire spelling — `RUNNING`, `SUCCESS`, `FAILED`, … | | `history` | string[] | yes | The state timeline, as stored: an array of state NAMES, e.g. `["CREATED","RUNNING","SUCCESS"]`. It carries no timestamps — see [`TaskRunDto::history`], which is written by the same function. | | `inputs` | object | yes | | +| `outputs` | object | no | The flow's `outputs:` as evaluated when the execution ended; absent when none were. A `SECRET` value is `******`. Only the single-execution `GET` fills it; the executions list does not carry it. | | `seq` | integer (int64) | yes | | | `execution_trigger` | object, nullable | no | The trigger record, `NULL` for an API start. | | `trigger_execution_id` | string (uuid), nullable | no | | diff --git a/platform/workflows/executions/detail.md b/platform/workflows/executions/detail.md index 5d32b09..a909528 100644 --- a/platform/workflows/executions/detail.md +++ b/platform/workflows/executions/detail.md @@ -88,13 +88,13 @@ The **Outputs** tab lists the task runs that produced outputs. Select one to see ![The Outputs tab showing the Return task's value and its expression](/img/platform/workflows/execution-outputs.png) -*No outputs* means no task run of the execution produced any. Flow-level outputs are not shown because they are not computed yet. +*No outputs* means no task run of the execution produced any. The tab lists task runs only: the flow's own [outputs](../flows/inputs-and-outputs.md#flow-outputs) are not shown here. `GET /api/ws/{workspace}/workflows/executions/{id}` returns them in an `outputs` field (a `SECRET` value is `******`); the executions list does not carry it. ## Limits and known issues - The **Overview** tab does not list inputs that came from `defaults:`. - Tasks inside a `Loop` have task runs in the child executions, not in the parent. Open a child to see them. -- Flow `outputs:` are not shown anywhere. +- Flow `outputs:` are not shown in the UI; read them from the execution API. ## Further reading diff --git a/platform/workflows/flows/error-handling.md b/platform/workflows/flows/error-handling.md index 74b2f1f..0846be5 100644 --- a/platform/workflows/flows/error-handling.md +++ b/platform/workflows/flows/error-handling.md @@ -38,6 +38,8 @@ The task above fails three times, one second apart. The **Gantt** tab draws each The three retry types, and `behavior`, are in the [YAML reference](./yaml-reference.md#task-retry). `warningOnRetry` is accepted and has no effect; the editor warns `ignored_property` when you save. +A `retry:` on a flowable task (a `Sequential`, a `Loop`, a `Parallel` and so on) is inherited by the tasks inside it; inside a `Loop`, by the tasks of each iteration. The nearest policy wins: the task's own, then its ancestors', then the flow's. See [Loop retry](./flow-control.md#loop-retry). When a task fails for good under such a policy, its parent now ends `FAILED`; before this release it stayed `RUNNING` forever. + ### Retry the whole flow A flow-level `retry:` starts a new execution when the flow fails: diff --git a/platform/workflows/flows/flow-control.md b/platform/workflows/flows/flow-control.md index b64ab3e..59bb127 100644 --- a/platform/workflows/flows/flow-control.md +++ b/platform/workflows/flows/flow-control.md @@ -150,12 +150,64 @@ The `If` and `Switch` above, with the `Parallel` and `Sequential` examples, make | `values` | A YAML list, a map, a JSON array in a string, or an expression such as `"{{ inputs.regions }}"`. An internal-storage URI runs one iteration per line of the file. | required | | `concurrencyLimit` | `1` runs iterations one at a time, in order. `N` runs up to N together. `0` runs them all together. | `1` | | `transmitFailed` | Whether a failed iteration fails the `Loop`. | `true` | +| `outputs` | Values to collect from each iteration. See [Loop outputs](#loop-outputs). | none | +| `fetchType` | `FETCH`, `STORE` or `AUTO`: how the collected outputs are kept. | `AUTO` | +| `retry` | A retry policy that the tasks of each iteration inherit. See [Loop retry](#loop-retry). | none | | `tasks` | The body, run once per item. | required | -**Each iteration is its own execution.** The parent execution shows only the `Loop` task. Each item starts a *child execution* with the flow's tasks inside the loop, and the parent's **Overview** tab lists them under **Child executions** with the iteration number and value. The tasks inside the body have no task runs in the parent, so their outputs are not reachable from tasks after the loop. +**Each iteration is its own execution.** The parent execution shows only the `Loop` task. Each item starts a *child execution* with the flow's tasks inside the loop, and the parent's **Overview** tab lists them under **Child executions** with the iteration number and value. The tasks inside the body have no task runs in the parent, so their outputs are not reachable from tasks after the loop unless the `Loop` collects them with `outputs:`. ![The Overview tab of a loop's execution listing three child executions, one per value](/img/platform/workflows/execution-children.png) +### Loop outputs + +Declare `outputs:` on the `Loop`, with the same `id`, `type` and `value` as [flow outputs](./inputs-and-outputs.md#flow-outputs), to collect a result from each iteration. The expressions are evaluated at the end of every iteration, after its `errors:` and `finally:`, and can read the iteration's task outputs and `{{ item }}`. + +```yaml +- id: per_region + type: io.kestra.plugin.core.flow.Loop + values: ["emea", "apac", "amer"] + outputs: + - id: size + type: INT + value: "{{ outputs.count.value }}" + tasks: + - id: count + type: io.kestra.plugin.core.output.OutputValues + values: + value: 1 +- id: total + type: io.kestra.plugin.core.log.Log + message: "{{ outputs.per_region.iterationCount }} iterations ran" +``` + +After the loop, `outputs.per_region.outputs` is a list with one entry per iteration: + +```text +{item: {value: "emea", iteration: 0}, outputs: {size: 1}} +``` + +`item.value` is the iteration's value as a string, `iteration` its zero-based index, and `key` is present only when `values` is a map. The list is in the order the iterations ended, which is index order with `concurrencyLimit: 1`. An iteration with no outputs adds no entry. + +Every `Loop` run, with or without `outputs:`, also has the counters `iterationCount`, `runningIterations` and `terminatedIterations` (a map from state to count) under `outputs.`. + +| `fetchType` | What the list holds | +|---|---| +| `FETCH` | The outputs map of each iteration. | +| `STORE` | One Ion file per iteration, `outputs.ion` in the iteration's storage; the entry holds `{uri: "kestra:///…"}` instead of the map. | +| `AUTO` (default) | `STORE` when `values:` is a string that renders to a `kestra://` URI, otherwise `FETCH`. The choice is made once per run. | + +Failure and limits: + +- An output that fails to render or convert fails its iteration: the iteration's history gets `FAILED` after its terminal state (`CREATED RUNNING SUCCESS FAILED`). With `transmitFailed: true` (the default) the `Loop` ends `FAILED` and that iteration's outputs are not added. An output with `required: false` that fails is `null` and logs a warning. +- One iteration's outputs may serialise to at most 1 MiB and the whole list to 16 MiB. Beyond either, the iteration fails with a message that points to `fetchType: STORE`. With `transmitFailed: false`, an iteration refused by the 16 MiB total is not added and counts under `FAILED` in `terminatedIterations`, although its own execution shows `SUCCESS`; the reason is in the `Loop` run's log. +- `SECRET` and `FILE` outputs follow the [flow output rules](./inputs-and-outputs.md#flow-outputs). A `SECRET` output with `fetchType: STORE` (or `AUTO` over a file) is refused when you save, because the file would hold the value in clear. Duplicate output ids inside a `Loop` are refused (`duplicate_output_id`). +- The parent flow's own `outputs:` are not evaluated inside a `Loop` iteration. + +### Loop retry + +A `retry:` on a `Loop`, or on a task that contains it, applies to every task and `Subflow` inside each iteration. When several policies could apply to a task, the nearest wins: the task's own `retry:`, then one on an ancestor inside the iteration, then the `Loop`'s or its ancestors', then the flow's. The `Loop` run itself is never retried. A `retry:` with `behavior: CREATE_NEW_EXECUTION` that reaches a task inside a `Loop` is saved with a `not_honoured` warning and refused when it runs. + ## LoopUntil `LoopUntil` runs its tasks, then checks `condition`. It repeats until the condition is true or a limit in `checkFrequency` is reached. The loop's own outputs are under `outputs.`, with `iterationCount`. @@ -190,7 +242,7 @@ This run logged `Check number 1`, `2` and `3` and then succeeded. `checkFrequenc b: 22 ``` -The subflow's id and namespace must exist in the workspace. A subflow that calls itself is refused (`self_subflow`). The task's outputs are `executionId`, `state` and `outputs`. Today `outputs` is always empty, because flow-level `outputs:` are not computed. With `transmitFailed: true` (default) a failed subflow fails the parent. A Subflow appears in the topology as a node you can expand. +The subflow's id and namespace must exist in the workspace. A subflow that calls itself is refused (`self_subflow`). The task's outputs are `executionId`, `state` and `outputs`. With `wait: true`, `outputs` is the called flow's [flow-level outputs](./inputs-and-outputs.md#flow-outputs), so `{{ outputs.sub.outputs.total }}` reads them. A called flow that declares outputs which were not stored (it ended before it ran, or its outputs failed) fails the `Subflow` run with `Failed to extract outputs with the error: …`. A `retry:` on the `Subflow` or on an ancestor applies to it. With `transmitFailed: true` (default) a failed subflow fails the parent. A Subflow appears in the topology as a node you can expand. The called flow: @@ -268,7 +320,6 @@ Script tasks need a container runner (`workflow.container_runner = docker`) and ## Limits and known issues - Flow-level `finally:` and `afterExecution:` never run. On flowable tasks only `AllowFailure` runs `finally`. See [Retries, errors and timeouts](./error-handling.md). -- A `Loop` does not expose its own `outputs:` yet. - `Pause` cannot hold child tasks, `errors` or `onPause`. - `LoopUntil` bounds must be literals. diff --git a/platform/workflows/flows/inputs-and-outputs.md b/platform/workflows/flows/inputs-and-outputs.md index 928d715..99a9fca 100644 --- a/platform/workflows/flows/inputs-and-outputs.md +++ b/platform/workflows/flows/inputs-and-outputs.md @@ -142,14 +142,33 @@ The **Outputs** tab of an execution lists each task run that produced outputs, w ### Flow outputs -A flow can declare `outputs:` with an `id`, a `type` and a `value`. The engine accepts them but does not compute them yet: no output appears on the execution, and a parent flow that calls the flow as a `Subflow` sees `outputs` as an empty map, so `{{ outputs.sub.outputs.total }}` fails. Pass data between flows with the KV store ([KV store](../namespaces/kv-store.md)) or with namespace files until this is implemented. +A flow can declare `outputs:` with an `id`, a `type` and a `value`. The engine evaluates them once, when the execution reaches its end, and stores the result on the execution. + +```yaml +outputs: + - id: total + type: INT + value: "{{ outputs.add.value }}" + - id: report + type: FILE + value: "{{ outputs.write.uri }}" +``` + +- **Where you read them.** `{{ execution.outputs }}` holds the map in every render after the end (before any `afterExecution:`). A parent flow that calls the flow as a `Subflow` with `wait: true` reads them as `{{ outputs.sub.outputs.total }}`. A Flow trigger reads them as `{{ trigger.outputs }}` in its `inputs:` (see [Flow and webhook triggers](../triggers/flow-and-webhook.md)). `GET /api/ws/{workspace}/workflows/executions/{id}` returns them in an `outputs` field. +- **Types.** Each value is converted by its `type`, with the same rules as inputs. An output with `required: false` that fails to render becomes `null` and a warning is logged; any other failure fails the execution. +- **Failure.** If an output cannot be rendered or converted, the execution ends `FAILED`: its history reads `CREATED RUNNING SUCCESS FAILED`, and the error is in the execution's log. +- **`FILE` outputs.** The value must be a `kestra:///` URI of a file that belongs to the execution itself or to its `Loop` iterations. A file of another execution, a trigger's or replay source's file, a namespace file, an `nsfile://` or `file://` URI or a malformed value is refused with `egret refuses output : is not a file of this execution`. A parent cannot re-publish its `Subflow` child's file as its own `FILE` output, though it can read it. +- **`SECRET` outputs.** The value is stored encrypted with `secrets.master_key`, is shown as `******` in the API and in every log line, and is revealed only to the execution's own expressions. A deployment without a master key fails the output and names the setting. `secret()` is refused in an output's `value:`. +- **Size.** All outputs of an execution together may serialise to at most 16 MiB; beyond that the execution fails with a message that points to a `FILE` output. + +An execution that ended before the upgrade has no outputs: only executions that end after it evaluate them. ## Limits and known issues - Only the run form validates inputs. Server-side validation of `required`, types and `values` is not implemented. - The outputs and rendered values of a task that uses a `SECRET` input are not masked; only the inputs and the logs are (KI-246). - `YAML` inputs are not parsed into an object in expressions. -- Flow `outputs:` are not evaluated. +- The UI does not show flow outputs: the **Outputs** tab lists task runs only. Read them from the API or from a parent flow. ## Further reading diff --git a/platform/workflows/flows/yaml-reference.md b/platform/workflows/flows/yaml-reference.md index fac7dfc..462d331 100644 --- a/platform/workflows/flows/yaml-reference.md +++ b/platform/workflows/flows/yaml-reference.md @@ -48,7 +48,7 @@ errors: | `variables` | map | Works | Constants for the flow, read as `{{ vars.name }}`. | | `tasks` | list, at least one | Works | The steps. See [Task catalogue](./task-catalogue.md) and [Flow control](./flow-control.md). | | `errors` | list of tasks | Works | Tasks that run when a task fails. See [Retries, errors and timeouts](./error-handling.md). | -| `outputs` | list | Accepted, not evaluated | Declared outputs of the flow. A flow's own outputs are not computed or shown, and a parent flow reading a subflow's `outputs` gets an empty map. | +| `outputs` | list | Works | Declared outputs of the flow, evaluated when the execution ends. Read as `{{ execution.outputs }}`, by a parent `Subflow` and from the execution API. A failure ends the execution `FAILED`. See [Inputs and outputs](./inputs-and-outputs.md#flow-outputs). | | `triggers` | list | Works for `Schedule`, `Flow` and `Webhook` | What starts the flow. See [Triggers](../triggers/index.md). | | `retry` | retry policy | Works | Runs the whole flow again as a new execution when it fails. See below. | | `disabled` | boolean, default `false` | Works | A disabled flow cannot be run and its triggers do not fire. | diff --git a/platform/workflows/triggers/flow-and-webhook.md b/platform/workflows/triggers/flow-and-webhook.md index e64e973..c4e8aa9 100644 --- a/platform/workflows/triggers/flow-and-webhook.md +++ b/platform/workflows/triggers/flow-and-webhook.md @@ -88,7 +88,7 @@ The route accepts `GET`, `POST` and `PUT`. A request with a multipart body is re - Task expressions cannot read `trigger.*`; pass what you need through `inputs:`. - Flow triggers fire from saved drafts; a draft with a Flow trigger listens at once. -- The `trigger.outputs` of an upstream flow are not available, because flow outputs are not computed. +- `{{ trigger.outputs }}` holds the triggering execution's [flow outputs](../flows/inputs-and-outputs.md#flow-outputs) and can be used in a Flow trigger's `inputs:`; it is not yet available in the fired flow's tasks. For a `dependsOn` trigger, the event's outputs are merged with the window's. If the merged outputs would pass 16 MiB the trigger does not fire on that event, the window is kept and the log names the sizes. ## Further reading