Documentation

The Two Formats

There are two syntax forms for expressions. The form used determines whether the full field value is replaced by the resolved value, or whether the resolved value is interpolated into a larger string.

Template Mode — @{directive:key}

The default mode. An expression directive can appear anywhere in a string field. Multiple directives can appear in a single field. The resolved value is interpolated in-place as a string.

"Hello, @{input:firstName} @{input:lastName}"
"https://@{env:API_HOST}/v1/endpoint"
"Token: @{secret:ServiceApiKey}"

When a template expression resolves to a non-string (number, boolean, object), the value is converted to a string for interpolation. If the entire field contains only a single expression with no surrounding text, the resolved type is preserved without conversion — see type coercion below.

Strict Mode — {{directive:key}}

Double-brace syntax. In strict mode the entire field must consist of exactly one expression — no surrounding text is permitted. The resolved value is returned with its original type preserved (no string conversion). Use strict mode when a field expects a non-string type such as a number, boolean, array, or object.

{{input:maxRetries}}        → resolves to integer 3, not "3"
{{output:fetchNode.items}}  → resolves to the original array
{{var:isEnabled}}           → resolves to boolean true

Directive Types

DirectiveResolves fromEvaluation TierExample
inputElement-level InputData bag (mapped from upstream outputs)Tier 2@{input:employeeId}
outputNodeOutputs in ExecutionMemory, keyed by element keyTier 2@{output:fetchUser.email}
varVariable scope chain (global + loop/function scopes)Tier 2@{var:approvalThreshold}
envProcess environment variablesTier 1@{env:DATABASE_URL}
secretVault / secrets storeTier 1@{secret:StripeApiKey}
contextExecution context (tenant, process, thread IDs)Tier 1 or Tier 2@{context:tenantId}

The Output Directive — Dot-Path Accessor

The output directive supports a dot-path accessor to navigate into nested objects. After the element key, append a dot-separated path to the value you need.

@{output:elementKey}               → full output dictionary
@{output:elementKey.fieldName}     → top-level field
@{output:elementKey.user.email}    → nested: output.user.email
@{output:elementKey.items[0].id}   → array index access

The path is evaluated using a simple recursive descent over the resolved object. If any segment in the path does not exist or the value at that point is null, the expression resolves to null (not an error). Use the ?? default operator in a JavaScript evaluator if you need a fallback value.

Three Evaluator Kinds

Beyond simple directive substitution, a field can declare a different EvaluatorKind via the NodeFieldDescriptor. This determines how the field value is processed after directives are resolved.

Template

The default. Directive placeholders are substituted into the string, then the result is returned as-is. No further interpretation. Suitable for most config fields.

JavaScript (Jint)

The field value is a JavaScript expression evaluated via the Jint interpreter. The resolved directive values are injected as named variables. Supports complex expressions: conditionals, arithmetic, string operations, array methods.

JSONPath

The field value is a JSONPath query applied against a specified source object (typically a node output or variable). Returns the matched values. Used for extracting data from complex JSON payloads.

JavaScript Evaluator — Available Context

When EvaluatorKind.JavaScript is used, the Jint sandbox exposes the following named identifiers. Any directive that was resolved in Tier 1 or Tier 2 is also injected by its key.

IdentifierContents
inputElement-level InputData dictionary
outputFull NodeOutputs dictionary (all nodes)
varsCurrent variable scope (read-only)
ctxExecution context (tenantId, processId, threadId)
nowCurrent UTC timestamp as ISO 8601 string
// Example JavaScript field value:
input.amount * 1.2 > vars.threshold ? "escalate" : "approve"

// Array operations are available:
output.fetchItems.items.filter(x => x.active).map(x => x.id).join(",")

Resolution Decision Flow

flowchart TD F["Field value string"] CHK1{"Contains\n@{...} or\n{{...}}?"} PLAIN["Return as-is\nNo expression processing"] CHK2{"EvaluatorKind?"} TMPL["Template Evaluator\nSubstitute all directives\nin-place"] JS["JavaScript Evaluator\nResolve directives first\nthen eval as JS expression"] JP["JSONPath Evaluator\nResolve directives first\nthen apply JSONPath query"] CHK3{"Strict mode\n{{...}}?"} TYPED["Return typed value\npreserve original type"] STR["Return string\ninterpolated result"] F --> CHK1 CHK1 -- No --> PLAIN CHK1 -- Yes --> CHK2 CHK2 -- Template --> TMPL --> CHK3 CHK2 -- JavaScript --> JS --> TYPED CHK2 -- JSONPath --> JP --> TYPED CHK3 -- Yes --> TYPED CHK3 -- No --> STR style F fill:#1e2640,stroke:#6c8cff,color:#e2e8f0 style PLAIN fill:#181d33,stroke:#2a3060,color:#94a3b8 style TYPED fill:#162d22,stroke:#34d399,color:#e2e8f0 style STR fill:#162d22,stroke:#34d399,color:#e2e8f0 style JS fill:#221c3e,stroke:#a78bfa,color:#e2e8f0 style JP fill:#221c3e,stroke:#a78bfa,color:#e2e8f0

Type Coercion

When a directive resolves to a non-string value and the surrounding field is a template string (not strict mode):

Escaping

To include a literal @{ in a field value without it being interpreted as an expression, double the @ sign:

@@{input:key}    → literal text: @{input:key}
@@{env:PATH}     → literal text: @{env:PATH}

This is only needed if you are constructing a field value that should contain the expression syntax as literal text — for example, generating a template string that will be evaluated again downstream.

Nested Expressions

Expressions cannot be nested — you cannot put an expression inside another expression's key. If you need to compute a key dynamically, use the JavaScript evaluator, which gives you full programmatic access to all bags.

Expressions are config-time strings Expression directives are written into node configuration in the workflow editor at design time. They are not a scripting language for business logic — that belongs in OnProcess. Expressions exist solely to wire runtime data into static configuration fields.