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

# Steps and assigns

> The token threaded through a run, pipeline steps, and the assigns map that nodes share

A running flow threads a single state object - the **token** - through every node in the graph. Nodes read values from the token and write values back to it. The most important part of the token is a string-keyed map called **assigns**; it is the primary way nodes share data.

## Assigns

An assign is a named value held on the token. Assigns are how downstream nodes see what earlier nodes produced. Typical patterns:

* A step that fetches employees from a source system writes the list to an assign named `employees`.
* A later node reads that assign, iterates over it, and writes a transformed list to `outgoing_employees`.
* A final step reads `outgoing_employees` and sends it to the target system.

Several nodes exist specifically for managing assigns:

* **Assign Read** reads one assign and exposes its value on an output port.
* **Assign Put** writes a value coming in on an input port to a named assign.
* **FlowAssign** writes a single resolved value to an assign as its own step.
* **FlowCompose** builds a struct payload from per-field mapping specs and stores it under an assign.
* **FlowDefault** falls back to a computed value when an assign is nil or missing.

**Assign Read** and **Assign Put** only make sense inside an inner Data Engine graph - the kind embedded in a [FlowTransform](/flow-engine/transformation/flow_transform), FlowFilter, or FlowSwitch - because they move a value between a port and an assign, and ports only carry individual values in Data Engine mode. At a flow's top level (flow mode), ports carry the token from one node to the next rather than individual values, so there is nothing on a port for these two to read from or write to. FlowAssign, FlowCompose, and FlowDefault are the flow-level counterparts: they run as flow nodes and write straight to the token's assigns.

Assign names are free-form strings. Choosing consistent names across a flow is important; the editor shows which assigns are written by which nodes, and the run log refers to them by name.

## Pipeline steps

A **step** is a named unit of work - an operation that reaches out to a source or target system, runs a calculation, or performs a side effect. The **FlowAction** node is how a step is invoked from a flow.

A FlowAction configures:

* The **step** to run. Steps are grouped by system in the picker (for example, "Fortnox: Get all projects"). Every step has a stable internal key that persists in saved flows; the display name can change without breaking existing flows.
* The **system integration** the step should target, when the step talks to an external system.
* The step's **options**. Each step declares its own typed option schema, and the editor renders a form from it.
* An **on error** policy. See [Execution and error handling](/flow-engine/concepts/execution-and-errors).
* An optional **cache mode**, described below.

When a FlowAction runs, it invokes the step and merges the step's results back into the token's assigns. Which assigns a step writes is declared by the step itself, so the editor can show what will be available downstream.

## Step caching

Some steps are expensive - fetching a large list from a source system, for example. When the same step with the same inputs would be called more than once in a single run, the result can be memoised.

Caching is **per-run**: the first invocation of a cached step populates the cache, and subsequent invocations with an equivalent cache key reuse the first result. Only the step's assigns are cached; log entries and errors are not.

Steps opt into caching by declaring a cache key. Not every step is cacheable; those that aren't simply run again each time they are invoked.

A FlowAction's cache mode can be set to **force**, which bypasses the cache and forwards a signal to the step so any internal caches inside the step's implementation can also be bypassed. This is the escape hatch for reruns that must see fresh data.

## Reading dated properties

Domain entities in Twine - Employee, Schedule, TimeReport, and so on - carry their history as [dated properties](/platform/data-model). At the flow level, dated properties are read and written through dedicated assign nodes:

* **FlowAssign**, **FlowCompose**, and **FlowDefault** can each read a dated property as a value source, with an optional as-of date. This is how a flow pulls a dated-property value out of an assign whose value carries that history: FlowAssign extracts a single value and names it, while FlowCompose reads several into the fields of a struct.
* **FlowDatedPropertyPut** writes one dated property onto a struct held in an assign.

There is no Attribute node at the flow level. The Data Engine's [Attribute](/data-engine/assigns/attribute) node reads dated properties too, but it only runs inside an inner Data Engine graph - for instance inside a [FlowTransform](/flow-engine/transformation/flow_transform) or [FlowFilter](/flow-engine/transformation/flow_filter) iterating a list, where the current item is the subject it reads from.
