Skip to content

Flows

Most machines have a single linear sequence of steps. For those, you place steps directly under implements and the runtime executes them in order. When a machine needs multiple execution paths, parallel processing stages, or reusable step groups, you use named flows.

Implicit main flow

For single-flow machines, put steps directly under implements:

implements
ask classify, using: "anthropic:claude-haiku-4-5"
with task "Classify this text"
returns
category as text
assuming
category: "general"
compute format
{result: steps.classify.category}

No flow keyword needed. This is the implicit main flow.

Named flows

When you need multiple execution paths, wrap them in a flows container:

implements
flows
flow extract
ask fetch, from: "@mashin/actions/http/get"
url: input.source_url
assuming
body: {records: []}
status: 200
flow transform
compute normalize
{cleaned: steps.fetch.body.records.filter(r => r != null)}
flow load
ask store, from: "@myorg/data/writer"
records: normalize.cleaned
assuming
written: true
compute result
{status: "complete", count: normalize.cleaned.length}

Each flow is a named group of steps. Flows execute in the order they are declared unless you use run or goto to control the sequence.

run and goto

run flow(name) calls a flow and returns to the current position (like a function call). goto flow(name) jumps to a flow and does not return (tail call).

implements
compute check
{needs_retry: input.attempt < 3}
decide route
when check.needs_retry
run flow(process)
otherwise
run flow(fallback)
flows
flow process
ask analyze, using: "anthropic:claude-sonnet-4-6"
with task "Analyze this data"
returns
result as text
assuming
result: "Analysis complete"
flow fallback
compute default_response
{result: "Max retries exceeded", status: "failed"}

Use run when you need to return and continue execution. Use goto when the current flow is done and execution should continue in another flow.

decide with flows

The most common pattern: use a decide step to route to different flows.

machine order_handler
accepts
order_type as text, is required
amount as number, is required
responds with
status as text
handler as text
implements
decide route_order
when input.order_type == "subscription"
run flow(handle_subscription)
when input.amount > 10000
run flow(handle_enterprise)
otherwise
run flow(handle_standard)
flows
flow handle_subscription
compute result
{status: "processed", handler: "subscription_team"}
flow handle_enterprise
compute result
{status: "pending_review", handler: "enterprise_sales"}
flow handle_standard
compute result
{status: "processed", handler: "auto"}

Error handling per flow

Each flow can have its own on failure block:

implements
flows
flow fetch
ask get_data, from: "@mashin/actions/http/get"
url: input.url
assuming
body: {}
status: 200
on failure
compute fetch_error
{status: "fetch_failed", error: error.message}
flow process
ask analyze, using: "anthropic:claude-sonnet-4-6"
with task "Analyze: ${steps.get_data.body}"
returns
result as text
assuming
result: "OK"
on failure
compute process_error
{status: "analysis_failed", error: error.message}

If a step in fetch fails, only the fetch flow’s error handler runs. The process flow’s error handler is independent.

When to use multiple flows

Use named flows when:

  • A decide step routes to different multi-step paths
  • An ETL pipeline has distinct extract, transform, and load stages
  • You want independent error handling for different parts of the machine
  • Steps are logically grouped and the grouping aids readability

Keep a single implicit flow when:

  • Steps execute linearly from top to bottom
  • There are fewer than 6 steps
  • There is no branching or error isolation needed

Try it

Build an ETL machine with three named flows: extract (fetch data via HTTP), transform (clean and reshape with compute), and load (store via a machine call). Add on failure to each flow with different error responses.

Next steps