Skip to content

State: let / vars / persist

Three state blocks, three purposes.

let:
base-url: "${config.endpoint}" # immutable, evaluated once at start
greeting: "Hello, ${user.name}!"
vars:
counter: !int 0 # mutable, transient (lost after run)
buffer: !str-list
persist:
cursor: !str # mutable, survives across invocations
last-sync: !str # auto-loaded at start, auto-saved on success
BlockMutable?LifetimeUse for
let:NoOne invocation, evaluated once at startAnything computed once at flow start.
vars:YesOne invocation, transientLoop accumulators, transient bookkeeping.
persist:YesAcross invocationsPagination cursors, last-seen timestamps, watermark IDs.

persist: values are auto-loaded at the start of each invocation and auto-saved when the flow completes successfully. They live under activity/persistence/ in the storage tree.

Assign with $name: syntax in steps:, or batch with set::

steps:
- $counter: "${= counter + 1}" # single assignment
- set: # batch — committed atomically
cursor: "${products.last-id}"
last-sync: "${= str(_invocation-id)}"
seen-count: "${= seen-count + len(products.rows)}"

Prefer set: over multiple $var: lines when updating several variables at once — it commits them atomically.

A flow that pages through a source and remembers where it left off between runs:

persist:
cursor: !str ""
steps:
- $page:
invoke: http.get
with:
Url: "https://api.example.com/items?after=${cursor}"
- set:
cursor: "${page.body.next-cursor}"

Next invocation picks up from the stored cursor automatically. Pair persist: with the delta: transformer for change-detection pipelines.