Documentation

The Bag Hierarchy

Data bags exist at two scopes. Thread-scope bags live for the entire thread run and are accessible across all nodes. Element-scope bags are created for a single node's execution and discarded when that node completes.

flowchart TD subgraph TS["Thread Scope — ExecutionMemory"] direction LR ID["InputData\nTrigger data · immutable"] VR["Variables\nScoped variable chain\nread/write"] NO["NodeOutputs\nPer-node results\nkeyed by element key"] NB["NodeResultBranches\nTyped branch outputs"] CA["Cache\nTransient scratch\nnot serialized"] end subgraph ES["Element Scope — ProcessElementExecutionContext"] direction LR RC["ResolvedConfig\n3-layer merged\nconfig bag"] EI["InputData\nMapped upstream\noutputs"] EO["OutputData\nThis node's\nresult"] LM["LocalMemory\nPrivate node\nscratch space"] HL["HIL Bags\nPreHilData · Hil\nsuspension data"] end TS -->|"element dispatch\ninput mapping"| ES ES -->|"node completes\noutput stored"| NO style TS fill:#181d33,stroke:#6c8cff style ES fill:#111425,stroke:#a78bfa style ID fill:#1e2640,stroke:#6c8cff,color:#e2e8f0 style VR fill:#1e2640,stroke:#6c8cff,color:#e2e8f0 style NO fill:#1e2640,stroke:#6c8cff,color:#e2e8f0 style NB fill:#1e2640,stroke:#6c8cff,color:#e2e8f0 style CA fill:#1e2640,stroke:#6c8cff,color:#e2e8f0 style RC fill:#221c3e,stroke:#a78bfa,color:#e2e8f0 style EI fill:#221c3e,stroke:#a78bfa,color:#e2e8f0 style EO fill:#162d22,stroke:#34d399,color:#e2e8f0 style LM fill:#221c3e,stroke:#a78bfa,color:#e2e8f0 style HL fill:#2d2616,stroke:#fbbf24,color:#e2e8f0

Thread-Scope Bags

All thread-scope bags live on ExecutionMemory — the central object that travels with the thread for its entire lifetime and is serialized when the thread is durably suspended.

InputData

PropertyValue
TypeIReadOnlyDictionary<string, object>
ScopeThread lifetime
MutableNo
Expression@{input:key}

The original trigger data that started the workflow. Set once when the thread begins and never changed. Contains whatever the triggering event provided: form submission fields, webhook payload, API request body, or scheduled trigger parameters.

At startup, InputData is also loaded into the global variable scope, so values are accessible via @{var:key} in addition to @{input:key}.

Variables

PropertyValue
TypeScoped via VariableScope chain
ScopeThread lifetime (global) or current block (loop/function)
MutableYes
Expression@{var:key}

Variables are the workflow's writable memory. A scope stack implements a chain-of-responsibility lookup:

Reading a variable walks up the scope chain until it finds the key, or returns null. Two write methods have different semantics:

When a loop or function scope exits, all variables local to that scope are discarded. Variables in parent scopes that were modified via SetVariable retain their values.

NodeOutputs

PropertyValue
TypeDictionary<string, object>
ScopeThread lifetime
MutableYes — each node appends its entry
Expression@{output:nodeKey.field}

Every time a node completes, its output data is stored here keyed by ProcessElementKey. This is what makes @{output:nodeKey.field} work — any downstream node can reference any previously executed node's output by its key. The key is the unique identifier assigned in the workflow editor (visible in the node's property panel).

NodeResultBranches

PropertyValue
TypeDictionary<string, INodeResultBranches>
ScopeThread lifetime
MutableYes — only stored when executor sets result.OutputBranches

A typed companion to NodeOutputs for nodes that produce structured branch outputs — for example, an HTTP request node that returns different typed shapes on success versus error. Most nodes do not use this bag.

Cache

PropertyValue
TypeDictionary<string, object>
ScopeThread lifetime — but NOT serialized on pause/resume
MutableYes

Transient scratch space. Use for values that are expensive to recompute but do not need to survive a pause. The cache is cleared when the thread suspends. If a value must be available after resumption, store it in Variables or NodeOutputs instead.

Element-Scope Bags

Element-scope bags live on ProcessElementExecutionContext and exist only for a single node's execution. They are created by the element layer before dispatch and discarded after the node completes (with the exception of OutputData, which is promoted to the thread-scope NodeOutputs).

ResolvedConfig

PropertyValue
TypeIReadOnlyDictionary<string, object>
When availableAfter Tier 1 config resolution completes
MutableYes — Tier 2 writes resolved expression values back into it

The result of the 3-layer config merge (Extension defaults → Connector config → ProcessElement config), with all applicable expression fields resolved. By the time OnEntryValidate fires in the executor, this bag contains final, usable values. Executor settings classes read from it via LoadAndValidateConfigAsync<TSettings>, which deserializes the dictionary into a typed settings object.

InputData (element-level)

PropertyValue
TypeDictionary<string, object>
When availablePopulated by the orchestrator before element dispatch
MutableYes — the node can read and write it
Expression@{input:key} in Tier 2 expressions

The node's input bag — data from upstream nodes mapped into this node's input schema. This is what @{input:key} directives in Tier 2 expressions resolve against. It is separate from the thread-level ExecutionMemory.InputData, which holds the original trigger data.

Two bags named InputData There are two distinct bags called InputData. The thread-level bag (ExecutionMemory.InputData) holds the original trigger payload and is immutable. The element-level bag (ProcessElementExecutionContext.InputData) holds outputs from upstream nodes mapped into this node's input, and is mutable. @{input:key} in Tier 2 expressions resolves against the element-level bag.

OutputData (element-level)

PropertyValue
TypeDictionary<string, object>
When availableAfter the executor completes
Promoted toExecutionMemory.NodeOutputs[elementKey]

The node's output. After the executor returns, this dictionary is stored in the thread's NodeOutputs keyed by the element key, making it available to downstream nodes via @{output:elementKey.field}.

LocalMemory

PropertyValue
TypeDictionary<string, object>
ScopeSingle node execution only — not visible to other nodes
MutableYes

Private scratch space for a node's internal state. Used to pass values between lifecycle stages within the same execution — for example, a resource handle opened in OnEntry and released in OnExit.

HIL Bags

PreHilData and Hil are specialized bags used by the HIL (Human-in-the-Loop) suspension flow:

For the full HIL suspension flow, see HIL Labels.

Quick Reference: Which Expression to Use

What you wantExpressionResolves fromWhen
Original trigger data@{input:key}Thread-level InputDataTier 1 or Tier 2
Upstream node output@{output:nodeKey.field}NodeOutputsTier 2 only
Workflow variable@{var:key}Variable scope chainTier 2 only
Environment variable@{env:KEY}Process environmentTier 1 only
Vault secret@{secret:Key}Secret storeTier 1 only
Execution context@{context:tenantId}Execution contextTier 1 or Tier 2