Skip to main content

Variable Expressions

Stateway fields that touch instance variables fall into one of three positions. The position determines whether the variables. prefix is optional, required, or forbidden — and getting it backwards is the most common source of a field that silently resolves to nothing.

The three positions​

PositionMeaningvariables. prefixExamples
Read (expression)The value is an expression evaluated against the variablesOptional — {{variables.x}} and {{x}} are equivalentioMapping.inputs[].source, conditionExpression, assignee, keyExpression
Write (name)The value is the name of the variable to store intoForbiddenioMapping.*.target, a dataObject name, searchable_variables[]
Query (path)The value is a path used to filter or projectRequiredsearch filter keys, text.fields[]

Read position​

{{variables.x}} is the canonical form used throughout the documentation and examples. {{x}} works identically — useful when a variable name would otherwise collide with variables/instance/now — but it isn't the form this documentation teaches. The reverse is not true for namespaces: a variable named variables, instance, session, or now is shadowed by the namespace in the short form, so {{session}} never reads a variable called session. Write {{variables.session}} instead. In conditionExpression, the {{ }} wrapper itself is optional.

The namespaces available (variables, instance, session, now, and field-specific ones like response or result) vary by field. session holds the identity of the Frontend Gateway user whose action is advancing the process ({{session.userId}}, {{session.userDisplay}}): who started the instance, completed the task or sent the event. Stateway takes it from the session, not from variables, and resolves it at each step, so a task created after another user's task sees the user who completed that task. It is empty when no end user is acting: REST API calls, timers, signals, messages and service task responses. See Assigning to Whoever Started the Instance for the full table and the fallback pattern. Rather than repeating that per field here, see Supported Elements & Fields — it's generated directly from the engine's field registry and is the source of truth for which namespaces resolve where.

Write position​

A write-position value is a bare variable name matching ^[A-Za-z_$][A-Za-z0-9_$]*$. For ioMapping.*.target and variableFilter.variables, the variables. prefix is rejected at parse time — write positions never take an expression, only a name. searchable_variables[] is validated only as a non-empty string (z.array(z.string().min(1))); a variables.-prefixed entry there is accepted, not rejected, it simply never matches any variable at runtime.

Query position​

A query-position value is a path such as variables.status or variables.applicant.email. The variables. prefix is required here — the opposite rule from read and write positions. See the Query Language reference for the full filter syntax.

The four micro-syntaxes​

Four distinct little languages coexist across these positions. They look similar (curly braces, dots, variable names) but are not interchangeable:

1. Braced expression (read position)

{{variables.amount > 100}}

The general-purpose form: variables, comparisons, and basic operations inside {{ }}. Used in conditionExpression, ioMapping.inputs[].source, assignee, keyExpression, and elsewhere — see Supported Elements & Fields for the full field-by-field list.

2. State filter (its own dialect, no braces)

instance.status == 'completed' and variables.loan_status == 'disbursed'

Used only in storeAccess.stateFilter on a dataStoreReference: no {{ }} wrapper, and a restricted operator set (==, !=, and, or, not). See Data Stores → State Filter for the full grammar.

3. Query path (required prefix)

variables.status

Used in search filter keys and text.fields[] — a path, not an expression, and the variables. prefix must be present. See Query Language.

4. Bare name (forbidden prefix)

orderId

Used anywhere a value is a variable name rather than a reference to its value — ioMapping.*.target, for example. variables.orderId is rejected here; write orderId.

Partial interpolation​

Some read-position fields accept a variable reference embedded inside a larger string — "Bearer {{variables.token}}" concatenates the resolved value into literal text. This applies to taskHeaders.headers/authorization, taskHeaders.url (variables/instance/now only, not credentials), ioMapping.inputs[].source (all element types that accept it), and ioMapping.outputs[].source on serviceTask/businessRuleTask/userTask/startEvent — but not on sendTask, where outputs[].source is a Literal field (see Supported Elements & Fields) and a {{...}} template there is rejected at publish time with a 400, not interpolated.

Condition-shaped fields (conditionExpression, stateFilter) do not support this: they require the entire field to be a single expression, not a string built by concatenation.

Credential names​

Credential names referenced via {{credentials.name}} follow their own, more restrictive pattern: ^[a-z][a-z0-9_]*$ — lowercase letters, digits, and underscores, starting with a lowercase letter. See Credentials for how credentials are created and referenced.

See also​

  • Core Concepts — the {{expression}} syntax and available namespaces
  • Credentials — creating and referencing {{credentials.name}}
  • Service Tasks — ioMapping and taskHeaders in practice
  • Human Tasks — assignee and candidateGroups/candidateUsers expressions
  • Task Assignment — the full assignment expression grammar