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

# Flows

> Browse Genesis flows, inspect definitions and runs, and review runtimes, data movement, step-level SQL or Python, errors, and sample outputs.

## Overview

**Flows** is the customer-facing workspace for reviewing Genesis flow definitions and recent run history. Use it to see step composition, scheduling, runtime placement, last-run status, and graph-based detail views for both definitions and individual runs.

<Note>
  In auth-enabled deployments, opening **Flows** requires `view_flows`.
  Granting a role access to specific Flows also implies `view_flows`, so the
  workspace stays visible when those object-level grants are present.
</Note>

## Interrupted copy recovery

If the server restarts while a `bulk_copy` or `sql` step is writing to an
external table, Genesis leaves the step **interrupted** and keeps
`rows_written` unknown. While a qualifying interrupted step is waiting, a
background check counts the rows currently held by its target within the first
six hours after the interruption and adds that evidence to the step summary.
For a `bulk_copy` that copied a whole named source table, the summary also
includes the source count. A query-driven copy is reported with the target
count only.

The check does not change the step status or turn the count into a
`rows_written` result. Append-mode targets can include rows from before the
step, so their target and source counts are not expected to match. Configure
the check with `jobs.flow_interrupted_copy_target_check.enabled` (default
`true`) and `jobs.flow_interrupted_copy_target_check.interval_seconds` (default
`120`; `0` or less disables the interval) in [Advanced Settings](/docs/Setup/Advanced-Settings).

## What the page supports

| Capability | Description |
| - | - |
| **Flows tab** | Browse all flows, filter by total, scheduled, active, or recent success, search by name, description, or tags, switch between list and tile views, and scan runtime badges that show whether a flow runs locally, in a sandbox, on Railway or EKS, on Snowpark, or on Databricks. When the selected run finishes in the detail view, its flow card and status counts reflect the finished status, completion time, and duration without waiting for the next list refresh. |
| **Run History tab** | Review recent runs across flows with trigger, start time, duration, and status. |
| **Definition inspection** | Open a full-screen inspector for a flow definition, review its DAG plus related data nodes, and click any step node to inspect configuration details. |
| **Run inspection** | Open a specific run to see live step status, duration, trigger, and per-step outputs while the run is active, including rows written when that metadata is available. The run picker lists recent runs, including runs still in progress; when the selected run is executing, its tab is labeled **Current Run**. A completed run is labeled **Latest Run** when it is the newest run, or **Run** when an older run is selected. Click step or data nodes on the run graph to open a focused inspect modal with inputs, authored logic (SQL/Python), output samples, and errors. |
| **Graph views** | Open dedicated graph views for flow definitions or specific runs when you want a larger DAG view with step nodes, read or write data links, and inferred data references. During an active run, edges from pending or running steps animate; completed-step edges are solid green, and failed-step edges are red and stop animating. |
| **Flow actions** | Run a flow immediately, pause or resume it, refresh the page data, or delete a flow when you have access. |
| **Projects** | Filter, sort, or group flows by their organizational Project, and assign or clear a Project from the flow detail view when you have edit permission. See [Projects](/docs/User-Guide/Projects). |
| **Manage Sources** | Open the page-header **Manage Sources** button (branch icon) to register Git repositories, sync managed flows, and review source health in a modal (`manage_flows`). Sync applies to the current bot; see [Multi-Instance Sync](/docs/Blueprint/Multi-Instance-Skill-Flow-Sync) for the cross-bot lifecycle. Hidden when `VITE_ENABLE_REMOTE_ARTIFACT_REGISTRY=false`. |

Managed source scan types can be added later, but removing an enabled scan type
from an existing source is not supported. Delete and recreate the source with
the desired scan types.

`include` and `exclude` selectors on a managed flows source narrow which
artifacts Genesis discovers and enables. Selectors without glob metacharacters
are exact matches; selectors with `*`, `?`, or `[...]` use shell-style glob
matching against `artifact_id`, `artifact_type:artifact_id`, `path`, and
`artifact_type:path`. `exclude` overrides `include` when both match. On the
next sync, flows that no longer match are retired: Genesis disables the flow,
removes its cron job from the scheduler, and clears managed dependency rows for
that source. Retirement applies only to flows this managed source created; a
local flow with the same name stays enabled and keeps its schedule.

## Git-backed vs Genesis-versioned flows

Flows synced from a managed Git repository show a green **git-backed** badge on the
detail header. Flows authored and versioned inside Genesis show **genesis-versioned**
(gray badge). The modes are mutually exclusive: git-backed flows cannot use Genesis
version-history promote, revert, restore-to-draft, or discard-working-copy actions
until you copy them to a standalone flow. A synced definition held because
`auto_promote_on_sync` is off has its own review-and-promote action; see
[Multi-Instance Sync](/docs/Blueprint/Multi-Instance-Skill-Flow-Sync#flows-with-auto-promote-off)
for that lifecycle.

Click the badge for a short explainer. On git-backed flows, **Fork to standalone →**
in the badge explainer opens a modal to name the new flow (defaults to
`<flow_name>-standalone`). The flow inspector also exposes a **Fork to standalone**
button for git-backed flows. API: `POST /api/flows/{flow_id}/fork` with body
`{"new_name":"<flow_name>"}` (requires `manage` on the source). Agents can use
`flow_action(action="FORK_TO_STANDALONE", params={"flow_id":"...", "new_name":"..."})`.

**Pause** and **Resume** remain available on git-backed flows. Enabling or disabling a
flow is runtime instance state, not a versioned definition change, so you can
emergency-stop a scheduled git-backed flow without forking first.

## Review a synced Flow change

When a managed source has `auto_promote_on_sync: false`, a flow with a waiting
change shows a pending-promotion status in its row and detail header. Open the
flow and choose **Review and promote** to inspect the incoming definition before
making it live. If a promotion was applied but its finishing step did not
complete, the header offers **Finish promotion**. These controls require admin
or `manage_flows` permission.

To review a new Flow that has no row in the Flows list yet, use **Manage Sources**
in the page header and open the source's waiting artifacts. A newly synced Flow
can be enabled as part of promotion; that option applies only to new Flows.

## Inspect a flow or run

From the **Flows** tab, click a row or flow name to open the definition inspector. Use the **Runs** picker to choose any recent run, including the current in-progress run, or click a last-run status badge or a row in **Run History** to open run details. If no runs exist yet, the picker shows **No runs yet**.

Definition and run graphs can also show dedicated data nodes for declared inputs and outputs, observed run outputs, and data references inferred from supported Python database and dataset calls. Hover a data node to see its direction, source, location, and, when available, rows written.

On the **Flows** page and in **Deployments → Flows**, clicking a **step** or **data** node on the definition or run graph opens a focused **inspect modal** with Input, Logic (SQL, Python, or bulk-copy metadata from the flow definition), Summary, Output samples, and errors. Use the branch icon in the header or open a flow from the list when you need the full multi-pane inspector with version history and scheduling controls. In the full inspector, clicking a data node still jumps to the related step details.

When you select a step node, Genesis can show:

* connection and execution metadata,
* the step's SQL query or Python code,
* run errors or output summaries,
* sample output data when the run returns previewable results, including single-object payloads or wider tables in the expanded preview.

When a step retries and eventually succeeds, the output summary records
`Succeeded after N attempts`. Failure payloads shown through the UI and API
also redact local absolute paths before they reach the client.

For a step that writes rows to an external target, a failed write keeps the
Genesis-composed refusal context—such as the step's mode, connection, table, or
statement label—whole, while masking any target-provided text attached to it.
For failures composed by the target, Genesis keeps the target's error codes and
any recognised column or query details, while masking quoted values, numbers,
and echoed statements on a best-effort basis. A quoted target column name from
the rows being written remains visible, compared case-insensitively against the
write's schema, so a failed copy can identify the column without exposing the
values beside it. If the message cannot be parsed confidently, Genesis withholds
the message and shows the codes instead. The full target error remains available
in the server log at `DEBUG` level; the masked or withheld form is what appears
in the run record, activity log, and retry notifications.

<Tip>
  SQL and Python logic panels include **Copy** and **Expand** controls. Data previews include an explicit <code>Expand</code> button that opens a larger modal for wide tables and longer payloads. When a step writes to a Genesis Dataset and no run sample was captured, Genesis can load a short live preview from the dataset (up to 50 rows) with a **View in Datasets** link. You can resize the inspector side panel by dragging its left edge. In graph views, dashed edges indicate data links and dashed borders indicate references inferred from code; selected steps show a blue outline on the graph. Step edges animate only while their source step is pending or running and the run is still in progress.
</Tip>

For Python step code, the inspector applies Genesis-aware highlighting to these flow references when they appear: `tools.call`, `flow.emit`, `from genesis_flow import ...`, `genesis_flow.llm`, `genesis_flow.tools`, `genesis_flow.flow`, `llm.chat`, and `llm.complete`.

<Note>
  Genesis SDK v1 does not replace the flow-step helpers on this page. Inside
  flow Python steps, keep using `genesis_flow.tools`, `genesis_flow.llm`,
  `genesis_flow.flow`, `genesis_flow.secrets`, and the flow-scoped tokens
  Genesis injects. `Genesis.from_environment()` is intended for
  `execute_python_code` and external Python programs for now.
</Note>

## Python-step LLM calls

Flow runs stay deterministic by default, but a Python step can make a
one-shot model call through `genesis_flow.llm.ask(...)` when the pipeline needs
classification, summarization, extraction, or another bounded LLM step. These
calls do not run the full chat agent loop; they route through the Genesis model
stack and bill like other base LLM usage. With cost accounting enabled, calls
without a real conversation thread are recorded as system work in the Cost &
Usage cube rather than as conversation-thread activity.

```python theme={null}
from genesis_flow import llm

result = llm.ask(
    prompt="Classify each row by industry.",
    data=rows,
    model_tier="fast",  # optional: frontier | main | fast
    provider=None,  # optional provider override
    response_format={"type": "json"},
)

classified_rows = result.output
usage = result.usage
```

Omit `model_tier` and `provider` to inherit the flow authoring thread's
defaults when available; otherwise Genesis falls back to the flow bot's default
tier and provider resolution. Legacy `model="fast"`, `model="main"`, and
`model="frontier"` are still accepted as tier aliases for older flow code.
`result.usage` includes the resolved `provider`, `model_tier`,
`configured_model`, `model`, `total_duration_ms`, and token counts when the
provider returns them.

### Chain the model output vs enrichment-only

Treat `genesis_flow.llm.ask` results as **forward** when the next SQL step,
Python step, condition, or UI state must parse structured output — use
`response_format` when you need JSON and validate before writing `result` or
`flow.emit`. Treat the call as **side-channel** when the text is narrative,
audit notes, or optional columns that no branch depends on; downstream steps
should succeed even if enrichment is empty.

Genesis ships the built-in **app-flow-llm-usage** AgentSkill (default
thread-start on new deployments) with the same guidance for Genesis Apps
(`/llm/call`, `/llm/direct`) and Flow Python steps.

## Team Wiki writes from flow steps

When `wiki.write_enabled` is `true` in AppConfig, a Flow Python step (or the
sandbox `execute_python_code` runtime) may call `wiki_write` through the flow
tool bridge if `wiki_write` is on the step's signed tool allowlist. Genesis
stamps each write with a `source_channel` provenance tag the agent cannot
override: `flow:<flow_id>` for normal flow steps, or `runtime:<execution_id>`
for sandbox Python execution (the bridge reuses the flow identity with
`step_name="execute_python_code"`). Interactive chat writes keep the default
`chat` tag. Background Librarian extraction from conversations is independent
and requires `wiki.librarian_enabled` in addition to `wiki.write_enabled`; see
[Advanced Settings → Team Wiki / Librarian settings](/docs/Setup/Advanced-Settings#team-wiki--librarian-settings).

## Persist step output

When a step declares an `output` target, Genesis expects the step result to resolve to tabular rows shaped as a `list` of objects. Python steps can return rows directly, or return a dictionary with a row field:

```python theme={null}
rows = [{"name": "Alice", "score": 10}]
result = {"rows": rows, "row_count": len(rows)}
```

Pair dictionary-shaped results with `output.from` so the runner knows which field to write:

```json theme={null}
{
  "target": "dataset",
  "dataset_name": "Scores",
  "from": "rows",
  "table_name": "scores"
}
```

Supported row result shapes include `result = rows`, `{"data": rows}`, `{"result": rows}`, `{"rows": rows}`, or a nested dictionary selected with a dotted `output.from` path such as `payload.rows`. SQL steps already return `list[dict]` rows directly, so omit `output.from` for SQL outputs; `from: "data"` and `from: "rows"` are tolerated for compatibility. If a step declares an output target but Genesis cannot extract rows, the run fails with an explicit extraction error instead of silently completing without a dataset write.

SQL result column casing follows the underlying connector and database conventions. Some backends, especially Snowflake-compatible paths, may return unquoted aliases such as `kind` and `n` as `KIND` and `N`. Use quoted aliases in SQL when exact column casing matters.

In run results, SQL reads report `Query returned N rows`, with `rows_written` set to the number of returned rows. SQL writes report `Statement wrote N rows` and set `rows_written` when the engine returns a recognized non-negative write count. If the engine does not report one, the summary says `Statement completed; rows written: not reported by the engine` and `rows_written` is `null`. Counts come from the write statement's result; rows returned by `RETURNING` are still available as output but are not treated as a write count.

For Databricks targets, a completed Flow step can also provide writer evidence to a Prover
comparison when its relation and run timing match the target's last-write reading and the
target snapshot is bound to that step's connection. See [how the Prover attributes a clean
table](/docs/Genesis-Prover/Agents-And-The-API#a-clean-table-is-not-yet-a-proven-pipeline).

For Genesis Dataset outputs, `table_name` controls the table inside the dataset snapshot. If you omit it, Genesis defaults to the explicit `dataset_id` when provided, otherwise a slug of `dataset_name` such as `Root Directories` -> `root_directories`. If `dataset_id` is provided and no dataset exists yet, Genesis creates that stable dataset id automatically. If only `dataset_name` is provided, Genesis derives the new dataset id from the same slug and falls back to a UUID only when that id already exists. This keeps flow-populated datasets queryable with predictable table names.

For synthetic data or other publish-style Iceberg flows, prefer one publish step per target table:

This sample targets the local catalog. In a Databricks App, the built-in
`iceberg.genesis_iceberg` connection is the persistent Genesis lakehouse on Unity
Catalog. Flow outputs that omit `connection_id` use that connection by default;
omit `namespace` as well to write to the App's lakehouse schema. An explicit
namespace must be that schema, because the App identity cannot access other
schemas. See [Databricks App deployment guidance](/docs/deployments/Databricks/Genesis-On-Databricks#genesis-as-a-databricks-app).

```json theme={null}
{
  "type": "python",
  "execution_target": "sandbox",
  "code": "rows = [...]\nresult = {\"rows\": rows, \"row_count\": len(rows)}",
  "output": {
    "target": "iceberg",
    "connection_id": "iceberg.genesis_iceberg",
    "namespace": "synthetic",
    "table": "orders",
    "mode": "replace",
    "context_output": "metadata"
  }
}
```

`context_output: "metadata"` keeps the full row payload out of downstream step context after the publish succeeds, while preserving `rows_written`, `output_target`, and `output_location`. Use `context_output: "sample"` when later steps need a small preview, and `context_output: "full"` only for small row sets that downstream steps truly need.

Flow Python steps can emit structured metadata with either `flow.emit({"key": value})` or `flow.emit("key", value)`. Downstream steps can read selected values through `context`/`steps`, or use explicit input mappings such as `inputs: {"previous_rows": "producer.rows"}`.

Use `depends_on` to declare dependencies; `needs` is accepted as an alias. SQL steps use `query`, with `sql` accepted as an alias for agent ergonomics. `on_failure` supports `continue`, `log`, `notify_thread`, `pause_schedule`, `retry`, and `stop`.

Set `run_concurrently: true` on consecutive independent steps to run them as a
batch. Genesis runs at most 8 steps in one batch and waits for every step in the
batch before starting the next step or batch. Use this for independent
`bulk_copy` steps when several tables share the same flow; do not use it when
one step reads another step's output. Within a batch, `bulk_copy` steps targeting
registered connections (`output.target: "external"`) execute up to 4 at once;
`bulk_copy` steps targeting Iceberg tables still execute one at a time.

## Move a table between registered connections

To copy a whole table between two registered connections, use a `bulk_copy` step.
The step reads the complete source relation in the worker and chooses the target
writer from the target connection type. Use either `source_table` for a table-wide
copy or `query` when the source relation needs a projection or filter:

```json theme={null}
{
  "type": "bulk_copy",
  "connection_id": "sqlite.baseball_sqlite",
  "source_table": "batting",
  "output": {
    "target": "external",
    "connection_id": "databricks.prover_smoke",
    "table": "eve_move_stg.batting",
    "mode": "replace"
  },
  "timeout_seconds": 1800
}
```

The step requires `connection_id`, plus either `source_table` or `query`. Set `mode`
to `replace` or `append`. The `output.target` can be `iceberg` or `external`; an
`external` target also requires `output.connection_id` and `output.table`. External
bulk writers are available for registered Snowflake, Databricks, and SQLite
connections. The default `timeout_seconds` is `1800`.
When Genesis validates a flow's dotted connection ids, it also checks the legacy
connection store, so seeded demo ids such as `sqlite.baseball_sqlite` are accepted.
If a connection store cannot be read, Genesis reports that id as unchecked rather
than claiming it is missing.
Genesis uses this value as the worker deadline, converting it to minutes
without imposing a one-minute minimum.

For SQL and `bulk_copy` steps, you can also use a bare registered connection
name such as `workspace_sqlite`; Genesis resolves its registered type before
executing the step. The same applies to `output.connection_id` for an external
target. If Genesis cannot resolve an external target's dialect, it refuses the
write rather than guessing how to quote SQL identifiers and values.

External output configuration refusals are reported as configuration errors,
including missing required fields, unsupported `mode` or `identifier_case`
values, invalid `output.table` names, and unresolved target dialects. These
errors show the step's configuration details instead of being withheld as
possibly row-bearing target text.

If a `bulk_copy` step is cancelled, Genesis tells the worker to stop and waits
up to 30 seconds for the worker's answer. The worker cancels any statement
already in flight and refuses the one final statement that would change the
target. Therefore, a copy stopped during staging leaves an existing target
unchanged, and a missing target is still missing. The run is marked `cancelled`
and its step summary says that nothing was written. A copy that finishes before
the cancellation is answered completes with its rows.

If cancellation arrives while the final target statement is already running,
the database may commit it before the abort takes effect. Genesis reports that
the target may have committed and tells you to verify it before relying on the
result or rerunning. A cancelled run does not retry the copy. If the step runs
past its wait time or the worker shuts down during staging, Genesis also stops
the copy before the final target statement. A hard worker shutdown can leave
the temporary stage, Volume, or scratch table behind; remove it after checking
the target. These are abandoned copies, not target-side failures.

For a Databricks target, add `output.staging_volume` with an existing Unity Catalog
Volume when you want to control staging. Use exactly three name parts:
`catalog.schema.volume`. When it is omitted, Genesis creates a temporary Volume
in the target schema and attempts to remove it after the copy.
`replace` uses an atomic target write for Databricks and a transaction for SQLite,
so a failed load does not first clear the previous target rows. When the target
is missing, Databricks creates it together with the loaded rows in the final
statement instead of creating an empty table first. If the source contains text values
beside numbers and the existing target column is numeric, Genesis checks the target
before staging and refuses the copy with the column names, target types, and text
value counts; it does not include the values themselves.

The result includes `rows_written` and `execution_mode`; `rows_written` is `null`
when the target does not report a count. When a Databricks bulk copy creates a
missing table and its final statement returns no count, Genesis retrieves the
created table's count with a `COUNT(*)` query. If that query fails,
`rows_written` remains `null`; the completed copy still succeeds. A failed
external write reports the count committed by the target, not the number read
from the source: it is `0` when the
target is unchanged, a partial count when earlier append batches committed, and
`null` when the target may be empty or incomplete. External target steps do not
store output row samples in the run record; use the target connection to inspect
the data. When SQLite source columns contain only empty values, bulk copy
preserves their declared SQLite types instead of inferring a type from those
values. For a source column that is numeric apart from empty-string (`''`)
values, SQLite writes each empty string as `NULL`. The step result includes
`values_nulled` with each affected column and its count, and the step summary
explains the conversion; check it before reconciling by row count alone.

For a SQLite source, a column with no non-`NULL` values (including a column in
an empty result) keeps its declared SQLite type when one is available, so a
valueless numeric column remains numeric. If a column has no value and no
declared type, it is written as text; `valueless_as_text` and the step summary
name that column. A source column containing text alongside numbers is also
written as text when the target accepts it; `text_beside_numbers` reports the
text count by column and the summary names the affected columns. A Databricks
target with an existing numeric column still refuses such a copy before
staging, as described above.

Bulk copy keeps `Infinity`, `-Infinity`, and `NaN` as numeric values for
Databricks, Snowflake, and Iceberg targets. SQLite stores infinities as `REAL`,
but bulk copy refuses a column containing `NaN` before opening the target
database because SQLite would store it as `NULL`; nothing is written. Replace
`NaN` with `NULL` or a number in the source query, or use a target that stores
`NaN`.

For a Snowflake target, use a two- or three-part `output.table` name. A two-part
name uses the connection's `CURRENT_DATABASE()` value; if no current database is
available, name the database explicitly. Snowflake stages the Parquet file in an
internal stage that Genesis creates and removes for that copy. `append` loads the
stage directly into the target. `replace` loads a transient scratch table first,
then atomically overwrites the target, so a failed copy does not leave the existing
target empty. When the target is missing, Snowflake loads a scratch table first
and creates the target with its rows in one final statement, instead of creating
an empty table first. Snowflake matches staged columns by name without regard to case.

Set `output.identifier_case` to `preserve` (the default), `upper`, or `lower` when
you want Snowflake to apply that case to the target and its newly created columns.
Databricks and SQLite accept only `preserve` on the bulk path. A connection type
without a bulk loader, or a bulk loader that cannot honor the requested identifier
case, fails the `bulk_copy` step; Genesis does not silently substitute the
row-by-row writer. The result's `execution_mode` identifies the native path, such
as `snowflake_stage_copy_into` for append or
`snowflake_stage_insert_overwrite` for replace.

The `get_connection()` handle in a Python step is not an alternative for this transfer:
it is read-only for this purpose, its response is hard-capped at 200,000 bytes, and a
larger read raises instead of truncating. It exposes `query(sql)` and
`cursor().execute(sql)`, but no connection-level `.execute()` or `commit()`. Use
`bulk_copy` for large transfers instead of materializing the source through a Python
step.

For a Lakebase connection using Databricks App identity, use a Flow SQL step or
local Python step rather than a Python sandbox step; see [Connections — Databricks
Lakebase connections](/docs/Setup/Connections#databricks-lakebase-connections).

See [Snowflake attribution](/docs/Integrations/Data%20Sources/Snowflake/Snowflake#identify-twin-scanner-queries).

## SQL step safety

Flow SQL steps can use the normal query surface, but Genesis now blocks
namespace-level `CATASTROPHIC` statements in both definition validation and
runtime safeguards. `DROP DATABASE`, `DROP SCHEMA`, `DROP CATALOG`, and
`CREATE OR REPLACE DATABASE/SCHEMA/CATALOG` require an interactive
`user_override`, so they are not allowed inside saved flow definitions or
unattended flow runs.

If you need one of those operations, run it directly through chat with
`_query_database` after explicit human approval instead of embedding it in a
Flow. Older saved Flows that already contain one of these statements now fail
closed at run time instead of executing it.

## Definition fingerprints

Genesis exposes a canonical `definition_sha256` fingerprint when you create,
inspect, update, revert, or validate a flow through `flow_action` (`CREATE`,
`GET`, `GET_VERSION`, `UPDATE`, `REVERT`, or `VALIDATE_DEFINITION`). The hash
covers portable execution fields — `description`, `parameters` /
`parameters_schema`, `steps`, `on_failure`, `tags`, and `schedule` when present
— and excludes the instance-local Flow name. Use it to confirm that a reviewed
candidate matches the definition you are about to apply, revert, or promote.

## Flow input parameters

Flow definitions can declare optional `parameters_schema` entries, or the
equivalent `parameters` alias, so each run starts with typed inputs instead of
only raw strings. Each parameter can define `type`, optional `default`, and
optional `required`. At run start, Genesis fills defaults, rejects missing
required values, and coerces primitive `int` / `integer`, `float` / `number`,
`bool` / `boolean`, and `str` / `string` inputs before exposing them in the
initial step context.

```json theme={null}
{
  "parameters_schema": {
    "n": {"type": "int", "required": true},
    "flag": {"type": "bool", "default": "true"}
  }
}
```

Equivalent shorthand using the `parameters` alias:

```json theme={null}
{
  "parameters": [
    {"name": "n", "type": "int", "required": true},
    {"name": "flag", "type": "bool", "default": "true"}
  ]
}
```

<Note>
  `execution_target: "sandbox"` runs inside an isolated container. Listing `/` from a sandbox shows the container filesystem, not the host computer. The local `execute_python_code` executor confines reads and writes to `BOT_GIT_PATH`, `BOT_REPOS_PATH`, and `BOT_SCRATCH_PATH` (import with `from genesis import ...`), including writes into the repo working tree; paths outside those roots are refused.
  For local Python process cleanup, see [Code Executor Tools](/docs/Genbot-Toolkit/Code-Executor-Tools).
</Note>

<Tip>
  For sandbox Python steps, prefer step-level `python_packages` when you need
  a small number of extra libraries instead of shelling out to `pip` manually
  inside your code. Genesis installs only missing packages, and when you keep
  the default sandbox image plus a supported data-engineering package set or a
  driver-bearing connection, it can auto-select the prebuilt
  `genesis-sandbox-data-eng:latest` image. For repeatable demos or heavier
  dependency stacks, still choose an explicit prebuilt image.
</Tip>

## Automation surface

| Endpoint family | Use |
| - | - |
| `/api/flows` | List, create, update, or delete flow definitions. |
| `/api/flows/{flow_id}` | Fetch a single flow definition. |
| `/api/flows/{flow_id}/fork` | Fork a git-backed flow to a new standalone genesis-versioned flow (`{"new_name":"<flow_name>"}`). |
| `/api/flows/runs/recent` | Populate recent run history across flows. |
| `/api/flows/{flow_id}/runs` | Trigger a run or list runs for a specific flow. |
| `/api/flows/{flow_id}/runs/{run_id}` | Retrieve detailed run status and per-step outputs for the inspector. |
| `/api/flows/{flow_id}/schedule` | Set or remove a flow schedule. |
| `/api/flows/repositories` | List, create, update, delete, and sync managed Flow Git sources (`manage_flows`). |
| `/api/flows/{flow_name}/workspace/*` | Start, edit, discard, and open MR/PR for a managed-source flow workspace (`manage_flows`). |
| `/api/flows/{flow_name}/bindings` | Read or update per-instance logical-ref bindings for a managed flow (`manage_flows`). |
| `/api/managed-artifacts/*` | List dependency rows, bind logical refs, list bind targets, and recheck source health (`manage_git`). See [Multi-Instance Sync](/docs/Blueprint/Multi-Instance-Skill-Flow-Sync). |

### Programmatic schedule updates

When you schedule a flow through `flow_action`, Genesis accepts either a
structured cron trigger object or flat cron fields. The flat form supports
`schedule`, `cron`, or `cron_expression`, with optional `timezone` that
defaults to `UTC`. Cron strings must contain exactly 5 fields in
`minute hour day month day_of_week` order. For structured Flow cron triggers,
use `minute`, `hour`, `day_of_month`, `month`, and `day_of_week`; Genesis
normalizes `day_of_month` to the scheduler's `day` field when it syncs the
saved Flow schedule. Genesis validates cron field ranges and IANA timezones
before it saves the schedule. A `SCHEDULE` call with an unrecognized `params`
key is refused without changing the schedule.

```text theme={null}
flow_action(
  action="SCHEDULE",
  params={"flow_id":"daily_sales","schedule":"0 9 * * *","timezone":"America/Los_Angeles"}
)

flow_action(
  action="SCHEDULE",
  params={"flow_id":"daily_sales","trigger":{"type":"cron","cron":"0 9 * * *","timezone":"America/Los_Angeles"}}
)
```

To remove a flow schedule, pass `trigger=null` and omit other schedule keys.
The REST route `PUT /api/flows/{flow_id}/schedule` uses the same cron payload
shape and also accepts `{"type":"interval","seconds":<positive_integer>}` when
you need a fixed interval instead of cron.

New or changed Flow schedules that run more often than hourly need a future
`end_date` inside `trigger`. Runs less than 5 minutes apart require an end date
within 7 days of creation or renewal; runs at least 5 minutes but less than an
hour apart require one within 30 days. Hourly and slower schedules may omit it.
Cron uses the shortest time-of-day gap, including midnight, even when calendar
restrictions separate actual runs. These checks run before saving a schedule
or selecting a historical version through revert or promotion.

For example, a temporary poller can use
`{"type":"interval","seconds":60,"end_date":"<future ISO timestamp within 7 days>"}`.
Supply the complete Flow trigger when renewing it, with the new end date.
For intentional ongoing automation, set `perpetual:true` and a nonempty
`perpetual_reason` explaining the lifetime owner, and omit `end_date`.
Managed Jira and ServiceNow polling Flows use this explicit intent and stop when
their configuration disables polling. Existing schedules remain operational
after startup until their schedule is edited; edits to unrelated Flow details
do not impose a new expiration.

Choose a cadence that matches the source's update rate. Frequent LLM-backed
work can consume tokens and encounter rate limits; a Flow can perform direct
polling without a new LLM decision for each run. For scheduler audit fields and
the `MODIFY_JOB` renewal operation, see
[Process Scheduler Tools](/docs/Genbot-Toolkit/Process-Scheduler-Tools#expiration-and-intentional-perpetual-schedules).

## Scheduled flows that use Connections

A flow step that uses a saved Connection resolves the connection's secret in the
context of whoever runs the flow. A manual run uses your identity; a **scheduled**
run executes as a system scheduler user instead. If that scheduler user has no
RBAC role bound to the connection, the scheduled run fails immediately with an
error like `Connection '<type>.<name>' is not available or has no secret access
key configured` — even though the manual run worked. This is a common
next-morning surprise when building a first scheduled pipeline.

To fix this before the first scheduled tick, grant the connection a role the
scheduler user holds (commonly `user`) under **Roles with Access**:

* In the UI: **Config → Connections → \[connection] → Edit → Roles with Access**.
* From an agent: `connections_action(action="UPDATE", connection_type="aws",
  name="my_conn", roles_with_access=["user"])`. `GET` returns the current
  bindings, and `rbac_action(action="LIST_ROLES")` lists valid role names.

See [Connections → Roles with Access](/docs/Setup/Connections#roles-with-access).

## A run started from a chat reports back to it

When an agent starts a Flow from a conversation, that run records which thread
and which bot launched it. The moment the run reaches a terminal status —
`completed`, `failed`, `cancelled`, or `interrupted` by a server restart — a
notice naming the run id, its kind and its outcome is delivered into that
conversation, and the agent's next turn sees it.

This is what lets an agent say "I'll report when it finishes" and mean it. It
applies on success as much as on failure: before it existed, silence meant
either "still running" or "finished and nobody said anything", which is not a
signal anyone could act on.

Two properties worth knowing:

* **The notice reaches an active thread on its next tool result.** If the agent is
  mid-turn when the run ends, the full run result is attached to the next tool
  result. If the thread is idle, the notice arrives as a new turn instead.
* **The notice includes the full run result.** It carries the same result that
  `GET_RUN` returns, so the agent does not need a second lookup before reporting
  on the run. **Flows → Run History** remains available for human inspection.

If the server restarts while a step is running, Genesis marks the step
`interrupted` and leaves `rows_written` unknown (`null`). The target may have
received the complete write, part of it, or none of it, so verify the target
before relying on the result or rerunning the step. A step that was still
`pending` never started and wrote nothing.

A run with no launching conversation — a scheduled run, or one started over the
API — has nobody to report to and stays silent here. This also applies when an
outside caller invokes a Flow through MCP or `POST /api/tool_call`: even if the
request carries a thread id, the caller has no Genesis conversation session to
receive a wake. Read the outcome with `GET_RUN`. Operator notifications below
cover unattended scheduled failures.

## Unattended failure notifications

Scheduled Flows that fail without anyone watching must still reach an operator.
Genesis now posts a baseline **in-app notification** when an unattended run ends
in `failed` status, independent of the Flow's `on_failure` setting. This is
separate from the opt-in escalation path (`on_failure: notify_thread` →
`flow_failure_notifications` outbox → Slack drain job), which stays silent on
stock configuration until you wire each layer.

When a notification fires, the bell shows title **Scheduled Flow Failed** and
a short message naming the Flow and how many scheduled runs have failed in a
row. Genesis keeps error text out of the message body (it can carry query
fragments) and stores a truncated copy in notification metadata for diagnostics.
Open **Flows → Run History** or the notification entity link to inspect the
failed run.

Notification policy is driven by `flows.failure_visibility` in App Config
(`config/config-example.yaml`):

| Field | Type | Default | Purpose |
| - | - | - | - |
| `enabled` | boolean | `true` | Master toggle for unattended failure notifications. |
| `unattended_triggers` | list | `["schedule"]` | Run triggers treated as unattended. Add `api` when external automation runs Flows without a human present. An explicit empty list means nothing is unattended. |
| `first_notify_after_failures` | integer | `1` | Consecutive failed runs before the first notification. |
| `repeat_every_failures` | integer | `10` | After the first alert, re-notify every *N* further consecutive failures. |
| `min_seconds_between_notifications` | integer | `3600` | Hard floor between notifications for the same Flow. `0` disables the floor. |

Genesis counts consecutive failures since the Flow last completed successfully.
`cancelled` and `interrupted` runs are skipped — cancelling one run does not
reset a genuine failure streak. Manual runs do not match the default unattended
trigger set.

A Flow that keeps failing on schedule does **not** ring the bell on every run.
The escalation ladder (`first_notify_after_failures`, `repeat_every_failures`)
and `min_seconds_between_notifications` floor cap how often Genesis creates a
new **Scheduled Flow Failed** notification. Between those ladder notifications,
failed runs stay deliberately quiet because the operator was already told about
the current failure streak. The scheduler subprocess also stands down its
generic job-failure bell for those quiet runs, so a Flow failing every minute
does not produce one generic alert per minute. If the Flow recovers and then
fails again, that new streak is treated as a fresh outage and can alert again.
Process restart or eviction of in-memory notification state fails open — an
operator who might not have been told may receive another alert.

After you see an alert, use the evidence-first RCA tools below — not the
notification prose alone — before stating a root cause.

## Flow failure investigation and RCA

When a Flow step exhausts retries or a run fails with `on_failure: notify_thread`,
Genesis can deliver a trusted failure alert that includes the durable run ID.
When the failed run carries an isolated presenter `demo_scope` (for example from
an RCA demo fixture prepared through the presenter routes), the alert also
includes a **Demo scope** line so operators can tell which presenter session
produced the failure.
Agents investigating that failure must use the evidence-first RCA tools in
`flow_tools` — not the alert prose alone — before stating a root cause.

Recommended workflow:

1. **`inspect_flow_failure(run_id=...)`** — load persisted execution evidence
   (task definition, error log, retry exhaustion, connection identity,
   preceding-step context) and receive `required_citations` (exact
   `label=value` strings to copy into finalization). Genesis stamps a
   thread-scoped inspection receipt in the current thread.
2. **Reconcile and correct** — derive the cause from the returned evidence,
   update the Flow to a newer version, and stage the exact correction against
   the inspected artifact.
3. **`run_flow_rca_verification(...)`** — the combined finalizer. Pass
   `failed_run_id`, `root_cause`, `evidence_citations` (every value from
   `required_citations`), and `remediation_summary`. Genesis runs or reuses a
   linked `rca_verification` run on the newer Flow version, then durably
   persists the corrected outcome and protected gold record. A successful
   terminal receipt (`task_durably_complete: true`) means the RCA is complete;
   restate the cited result in the next message and do not call
   `record_flow_rca_outcome`.
4. **`record_flow_rca_outcome(...)`** — use only when correction cannot run yet
   (`remediation_status='staged'`) or when manual recovery is needed after a
   completed linked verification. Staged outcomes do not create protected gold.

Protected gold is immutable once written. If a follow-up model response fails
after verification succeeds, retrying `run_flow_rca_verification` or
`record_flow_rca_outcome` returns the canonical completed outcome without
overwriting the protected record. If a downstream tool reports a missing or
mismatched inspection receipt, call `inspect_flow_failure` again in the same
thread before retrying finalization.

For tool parameters and response fields, see
[`flow_tools` RCA tools](/docs/Genbot-Toolkit/Additional-Tool-Groups#flow-tools)
in the Genbot Toolkit.

## Sharing flows across instances

If your organization runs multiple Genesis instances, flows can be sourced
from a shared Git repository so they propagate to every instance through
normal pull-request review. The flow YAML uses logical refs like
`@snowflake.warehouse` that resolve per-instance via local bindings, so the
repository file stays portable. See
[Multi-Instance Skill, Flow & App Sync](/docs/Blueprint/Multi-Instance-Skill-Flow-Sync).

Flow definition files must be valid YAML. Quote scalar values that contain a
colon, such as `description: "Global policy: all X"`, so discovery and sync can
read the managed source consistently.

## Related pages

* [Multi-Instance Skill, Flow & App Sync](/docs/Blueprint/Multi-Instance-Skill-Flow-Sync) — share Git-backed flows across instances.
* [Scheduler](/docs/User-Guide/Scheduler)
* [Digital Twin and Context Graph](/docs/User-Guide/Digital-Twin-And-Context-Graph)
