---
title: "Flow"
description: "A Flow is the top-level unit of work in Dex. A Flow type describes one kind"
canonical_url: "https://docs.superdurable.io/concepts/flow/"
source: "docs/content/concepts/flow/index.mdx"
locale: "en"
---

# Flow

A **Flow** is the top-level unit of work in Dex. A Flow type describes one kind
of business process, such as an order, a subscription, or an approval. A Flow
run is one instance of it, such as order 42.

Dex owns the Flow's lifecycle and state. Your code does not need a database
or a job queue to keep the process going across crashes and restarts.

<a id="when-to-use"></a>

## When to use a Flow

Use one Flow for each business identity whose process Dex should own. Put work
that shares that identity and lifecycle into [Steps](https://docs.superdurable.io/concepts/step.md) of the
same Flow. Steps can run in parallel inside one Flow.

Start a separate Flow when the work has its own owner and lifecycle. Two Flows
coordinate by calling each other's RPCs. Use a [SubFlow](https://docs.superdurable.io/concepts/primitives/subflow.md)
when a parent Step waits for a child Flow, for example to split complex logic
into smaller Flows or to fan out work.

<a id="guarantees"></a>

## Guarantees

- A Flow ID has at most one active run at a time.
- What one Step method writes and publishes takes effect all at once, or not at
  all. See [Step guarantees](https://docs.superdurable.io/concepts/step.md#guarantees).
- With the default SYNC durability, a Step's result is stored durably before
  the next Step starts. It survives Worker crashes and Dex Server restarts.
- When no Worker is free, a Step waits in a durable queue. It runs when a
  Worker becomes available.

<a id="implement-a-flow"></a>

## What a Flow contains

You define a Flow by implementing the **Flow** interface in your language. It
declares three things:

- <a id="steps"></a>**Steps**: the start Step and every other Step the Flow can reach.
  [Steps](https://docs.superdurable.io/concepts/step.md) run the background work.
- <a id="persistence-schema"></a>**Persistence schema**: the [Attributes](https://docs.superdurable.io/concepts/primitives/attribute.md) and
  [Channels](https://docs.superdurable.io/concepts/primitives/channel.md) that hold the Flow's durable state. Dex
  rejects reads and writes to names that are not declared.
- <a id="rpc-handlers"></a>**RPC handlers**: methods that a client calls on a running Flow. See
  [RPC](https://docs.superdurable.io/concepts/primitives/rpc.md).

The start Step's input type is also the Flow's start input type. This Flow has
two Steps, one Attribute, one Channel, and one RPC.

**Python**

```python
status = Attribute("status", str)
notify = Channel("notify", None)


class ExampleFlow(Flow[int]):
    def __init__(self) -> None:
        self.finish = Finish()
        self.example = Example(self.finish)

    def get_steps(self) -> StepList[int]:
        return StepList.start_step(self.example).other_steps(self.finish)

    def get_persistence_schema(self) -> PersistenceSchema:
        return PersistenceSchema.of(status, notify)

    @rpc
    def describe(self, context: Context) -> RPCResult[str]:
        return RPCResult.of(status.get(context))
```

Example: [examples/python/dex_examples/primitives/flow/example_flow.py](https://github.com/superdurable/dex/tree/main/examples/python/dex_examples/primitives/flow/example_flow.py)

**Go**

```go
type ExampleFlow struct {
	dex.FlowDefaults
}

func (*ExampleFlow) GetSteps() []dex.StepDef {
	return []dex.StepDef{
		dex.DefineStartStep(Example{}),
		dex.DefineStep(Finish{}),
	}
}

func (flow *ExampleFlow) GetRPCs() []dex.RPCDef {
	return []dex.RPCDef{dex.DefineRPC(flow.Describe, nil)}
}

func (*ExampleFlow) GetPersistenceSchema() dex.PersistenceSchema {
	return dex.PersistenceSchema{
		Attributes: []dex.AttributeDef{Status},
		Channels:   []dex.ChannelDef{Notify},
	}
}

func (*ExampleFlow) Describe(ctx dex.Context, _ dex.None) (*dex.RPCResult[string], error) {
	value, err := Status.Get(ctx)
	if err != nil {
		return nil, err
	}
	return &dex.RPCResult[string]{Output: value}, nil
}
```

Example: [examples/go/primitives/flow/workflow.go](https://github.com/superdurable/dex/tree/main/examples/go/primitives/flow/workflow.go)

**Java**

```java
public class ExampleFlow implements Flow<Integer> {
    public static final Attribute<String> status = Attribute.define("status", String.class);
    public static final Channel<Void> notify = Channel.define("notify", Void.class);

    @Override
    public StepList<Integer> getSteps() {
        return StepList.startStep(exampleStep).otherSteps(finishStep);
    }

    @Override
    public PersistenceSchema getPersistenceSchema() {
        return PersistenceSchema.of(status, notify);
    }

    @RPC
    public RPCResult<String> describe(final Context context) {
        return RPCResult.of(status.get(context));
    }
}
```

Example: [examples/java/src/main/java/io/superdurable/dex/primitives/flow/ExampleFlow.java](https://github.com/superdurable/dex/tree/main/examples/java/src/main/java/io/superdurable/dex/primitives/flow/ExampleFlow.java)

**TypeScript**

```typescript
export class ExampleFlow implements Flow<number> {
  public getSteps() {
    return StepList.startStep(this.example).otherSteps(this.finish);
  }

  public getPersistenceSchema(): PersistenceSchema {
    return { attributes: [status], channels: [notify] };
  }

  @rpc({ outputCodec: stringCodec })
  public describe(context: Context): RPCResult<string> {
    return { output: status.get(context) ?? "" };
  }
}
```

Example: [examples/typescript/src/primitives/flow/example-flow.ts](https://github.com/superdurable/dex/tree/main/examples/typescript/src/primitives/flow/example-flow.ts)

**Rust**

```rust
impl Flow for ExampleFlow {
    type StartInput = i32;

    fn steps(&self) -> StepList<'_, Self::StartInput> {
        StepList::start(&self.example).and(&self.finish)
    }

    fn persistence(&self) -> PersistenceSchema {
        PersistenceSchema::new()
            .attribute(&status())
            .channel(&notify())
    }

    fn rpcs(&self) -> RpcList<Self> {
        RpcList::new().function(DESCRIBE, Self::describe)
    }
}
```

Example: [examples/rust/src/primitives/flow/flow.rs](https://github.com/superdurable/dex/tree/main/examples/rust/src/primitives/flow/flow.rs)

<a id="identifiers"></a>

## Identifiers

The word Flow can mean a Flow definition or a Flow run. These
identifiers keep them apart.

| Identifier          | Who sets it                           | Meaning                                                                                                                                          |
| ------------------- | ------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| **FlowType**        | Application (via Flow implementation) | Stable type name Dex uses to route Worker calls and search results.                                                                              |
| **Flow ID**         | Application client on start           | Business identity for one Flow run. The ID reuse policy controls whether another run may start with the same Flow ID.                            |
| **Run ID**          | Dex Server                            | Identity of one run of the Flow ID. Time travel keeps it and creates a new run attempt. Address a Flow by its Flow ID; the Run ID is diagnostic. |
| **StepType**        | Application (via Step implementation) | Stable name for a Step definition inside the Flow.                                                                                               |
| **StepExecutionID** | Dex Server                            | One execution of a Step type, formatted as **StepType-Number**.                                                                                  |

Most SDKs use the language's type name as the **FlowType** unless you
override it. Do not change these names while runs are open.

<a id="lifecycle"></a>

## Lifecycle

A [Client](https://docs.superdurable.io/concepts/client.md) starts a Flow with a Flow ID and the start
input. Dex then runs the start Step on a Worker. Each Step returns a decision
that moves to the next Steps or closes the Flow. Several Steps can be active
at the same time.

A Flow closes in one of these ways:

- a Step completes or fails it;
- its timeout fails or cancels it;
- a client stops it;
- a new start with the same Flow ID terminates it. This happens only when the
  new start uses an ID reuse policy that terminates the running Flow. A SubFlow
  started with **ALWAYS_RESTART** terminates an earlier child the same way.

Instead of failing or canceling the Flow, a timeout can run a handler that
decides the outcome. While the Flow runs, clients read and change it through
its RPCs.

<a id="start-options"></a>

## Start options and configuration

Two sets of options shape a Flow run:

- **StartFlowOptions** apply once, at start. They set the Flow timeout, the ID
  reuse policy, the start request ID, and initial Attribute values.
- **FlowConfig** holds runtime settings, such as the default Step durability.
  You can update it while the Flow runs.

Start a Flow directly with a stable business Flow ID. Do not read or search
first to check whether it already exists. To retry one start safely, do three things. Use an
ID reuse policy that rejects a second run. Send the same request ID on every
retry. Set the start option that returns the existing run instead of an
already-started error. Then a retry of a start that already succeeded returns
that run. With a policy that allows another run, a retry can start a second
run instead.

## Reference

The SDK reference covers:

- the [Flow](https://docs.superdurable.io/reference/python/flow.md) definition;
- [StartFlowOptions](https://docs.superdurable.io/reference/python/start-flow-options.md) and
  the [Flow timeout and timeout handler](https://docs.superdurable.io/reference/python/start-flow-options.md#timeout-and-timeout-policy);
- [Flow ID, RequestID, and already-started handling](https://docs.superdurable.io/reference/python/start-flow-options.md#flow-id-request-id-and-already-started);
- [StartFlow best practices](https://docs.superdurable.io/reference/python/start-flow-options.md#start-flow-best-practices);
- [FlowConfig](https://docs.superdurable.io/reference/python/flow.md#flow-config) and how to update
  it.
