# Workflows

> Draw a pipeline on the builder canvas, wire its steps together, save it as a version, and activate it.

A workflow is a graph. Steps are the boxes, and an arrow from one box to
another means "wait for that one to finish, then run". Hodoflow executes the
graph, not a script, so anything without an arrow between it and another step
runs in parallel.

**Workflows** in the sidebar lists everything you have saved. **New Workflow**,
or opening an existing one, takes you to the builder. The builder is desktop
only — on a phone you get the read-only graph view instead.

## The canvas

A brand-new workflow opens on the trigger wizard, because a workflow with
nothing to start it can never run. Pick **Webhook**, **Scheduled**, **Kafka**,
or **No trigger (subflow)**; the next screen configures whichever you chose.
[Triggers and webhooks](/help/workflows/triggers-and-webhooks) covers all of them.

Steps come from a searchable palette rather than a drag-and-drop tray. Press
`Cmd`/`Ctrl` + `K`, or click the `+` beneath a selected step, or click the `+`
under the **Start here — add the first step** card on an empty canvas. Type in
**Search steps…** and the palette narrows; a **Recent** section remembers what
you reached for last.

The `+` under a selected step also offers **On error…** and **Compensate…**.
Those add the same step types, wired with a different kind of arrow: an
on-error edge runs its target only when the parent fails, and a compensate edge
runs its target to undo work when the run unwinds. You can change any existing
arrow between **Normal**, **On error**, and **Compensate** by right-clicking it.

Connections are drawn by dragging from one step's handle to another's. While
you drag, every step the connection cannot legally reach outlines itself in
red — that is either a cycle, or a step already reachable from where you
started. Hodoflow keeps the graph free of arrows that another path already
implies, so adding a shortcut can silently remove one; a toast at the bottom of
the screen tells you when that happened and why.

A step with no arrow coming into it shows **No parent step** on the card and
counts toward the **invalid nodes** total in the toolbar. It will not stop you
saving, but it will stop you activating.

Click a step to open the inspector on the right. **Name** is the label you see
on the canvas, **API Name** is the identifier formulas use to reach this step's
output, and the **Configuration** and **Policy** tabs hold everything else.
Select several steps and the panel switches to **Duplicate**, **Copy**, and
**Delete** for the whole selection.

Positions do not matter. Only the connections are saved, and Hodoflow runs its
own layout every time you open a workflow — **Auto Layout** in the toolbar
re-runs it on demand and never counts as a change.

## Step types

Thirteen step types exist. Seven have an entry in the step palette and can be
added to a workflow today; the other six run in the engine but have no way into
the builder yet. The identifier in the first column is what the **Type** column
shows when you inspect a run.

| Step type | Palette name | What it does |
|---|---|---|
| `http_request` | **HTTP Request** | Calls any HTTP endpoint and passes the response to the steps below. |
| `api_request` | **API Request** | Calls one operation from an imported API Spec, with its parameters typed for you. |
| `harmonizer` | **Map to Data Model** | Maps incoming fields onto a data model and saves the result to the warehouse as a record. |
| `filter_list` | **Filter List** | Keeps only the items of an upstream list that match a condition, and skips the branch below when nothing survives. |
| `data_transform` | **Data Transform** | Reshapes, renames, and computes fields with formulas before the next step reads them. |
| `conditional` | **Conditional** | Evaluates its branch conditions top to bottom and sends the run down the first one that matches, or down **Default**. |
| `subflow` | **Subflow** | Runs another workflow as a run of its own and continues with whatever it returns. |
| `delay` | — | Parks the run until a fixed instant and frees the slot until then. The deadline is recorded when the run suspends, so a resume never restarts the clock. |
| `kafka_produce` | — | Publishes one message to a Kafka topic through a Kafka connector. |
| `snowflake_query` | — | Runs a `SELECT` against Snowflake through a Snowflake connector. |
| `snowflake_write` | — | Writes rows into a Snowflake table, by insert, merge, or truncate-and-insert. |
| `s3_read` | — | Downloads one object, or lists objects under a prefix, from S3-compatible storage. |
| `s3_write` | — | Writes one object to S3-compatible storage. |

### Running a step once per item

**HTTP Request**, **API Request**, **Data Transform**, and **Subflow** each get
an **Iteration** section in the inspector. Leave **Mode** on
**Run once with the whole payload** and the step behaves normally. Switch it to
**Run once per item** and you get:

- **Source list** — the path to the list to walk, like `fetch.data` for an
  upstream step's whole output or `fetch.data.stations` for a list inside it.
  Each element is available to formulas as `item`. Upstream lists Hodoflow can
  already see are offered as clickable chips.
- **When an item fails** — **Fail the run**, or **Skip and count**, which
  attempts every element and lets the step succeed on the rest.
- **Advanced** — how many items run at once, a delay between them, and a
  **Max items** ceiling that refuses the step before any item runs.

## Saving and versions

Nothing is written until you press **Save**. Until then the toolbar shows
**Unsaved changes**, and you can rearrange, delete, and reconnect freely.

Every version of a workflow is its own record, sharing a group with the others.
Editing a version that has been published turns the button into
**Save as New Version** and the indicator into **Draft changes**: your edits
fork a new version rather than rewriting the one that is live. A version that
was never published keeps taking plain saves.

The version number sits beside the workflow name in the header as `v1`, `v2`,
and so on. **Edit details** changes the name and description without touching
the graph.

Back on the **Workflows** list, each workflow collapses into one row with a
version count. Expand it and every version is there with its state — **Active**,
**Inactive**, or **Draft** — plus **Published** date, and **Activate**,
**Deactivate**, **Delete**, and **Open** as appropriate. Deleting a version
deletes its runs too, and cannot be undone.

## Activation

A saved workflow is not a running workflow. Press **Activate**.

The button stays disabled while anything obvious is wrong, and its tooltip says
which thing: *Save changes before activating*, *Fix invalid nodes before
activating*, *Add at least one trigger before activating*, *Add at least one
step before activating*, *Connect every step to the flow. Some have no incoming
path*, or *Re-sample stale sources before activating*.

When you press it, Hodoflow checks the version properly and refuses with a
message naming the first problem it found:

- A workflow must have at least one trigger, unless it is a subflow. Subflows
  are invoked by other workflows, so they are exempt.
- Every step must be fully configured.
- Every **Map to Data Model** step needs field mappings, and its data model
  needs a primary key field that is mapped.
- The steps must connect to the trigger, with no disconnected steps.
- The graph must have no cycles.
- No source schema may be stale. If a source's configuration changed since it
  was last sampled, a banner offers **Re-sample** per source.

Activating a version deactivates whichever version of that workflow was live
before, so exactly one version of a workflow ever runs.

**Deactivate** takes it offline: webhooks for that workflow start being
rejected, its schedules are cancelled, and its Kafka consumers stop. Runs
already in progress continue to completion. Deactivating is always allowed and
never forks a version.

## Watching runs

**Workflows → Monitor** is the run history. Its table shows **Time**,
**Workflow**, **Status**, **Duration**, and **Source**, filtered by workflow
version and by time range. Turn on **Live Tail** and new runs stream in as they
happen.

A run's status is one of `pending`, `running`, `completed`, `failed`, or
`suspended`. Suspended is normal — it is what a run looks like while it waits
out a delay or waits for a subflow.

Click a run to open its steps in order, each with its own status, duration, and
error. A failed run also gets a **Run again from the failure** panel with a
**Retry Run** button. Every step that finished is kept, and only the steps that
never finished run again.

Occasionally a run stops between issuing an external call and recording what
came back, and the panel above the retry asks you to say what happened under
**Unaccounted-for external calls**. Check with the provider, then answer
**Never sent** — which lets the step run again — or **Went through**, which keeps
the failure rather than repeating the call. Retry stays disabled until you do,
because Hodoflow cannot know whether repeating the call is safe.

The **Analytics** tab on the same page totals **Total Runs**, **Completed**,
**Failed**, and **Avg Duration** for whatever the filters are set to, with
breakdowns by workflow and by status.

From inside the builder, the **Monitor** button in the toolbar opens the same
page pre-filtered to the workflow you are editing.
