Skip to main content

Variable Resolution System

apptor flow has two distinct variable syntaxes. Using the wrong one is the most common mistake when building workflows.


The Two Syntaxes

ContextSyntaxEngineWhere
Node property values{varName}TemplateVariableResolversystemPrompt, prompt, recipientAddress, subject, body, script, any text property
Condition expressions${expression}JUEL enginecondition.expression field on If/Else and Loop nodes only

Correct Usage

# In an AI Task systemPrompt:
✅ "You are helping {customerName} with ticket {ticketId}."
❌ "You are helping ${customerName} with ticket ${ticketId}."

# In an Email body:
✅ "Dear {firstName}, your invoice {invoiceNumber} is attached."
❌ "Dear ${firstName}, your invoice ${invoiceNumber} is attached."

# In an If/Else condition expression:
✅ ${issueType == 'critical'}
❌ {issueType == 'critical'}

# In a Loop condition:
✅ ${iteration < maxRetries}

Template Variables — {varName}

Resolved by TemplateVariableResolver in apptor-flow-sdk.

Pattern: \{([a-zA-Z_][a-zA-Z0-9_]*)\}

Rules:

  • Variable names start with a letter or underscore
  • Variable names can contain letters, digits, and underscores
  • Case-sensitive: {OrderId}{orderId}
  • Unknown variables resolve to an empty string (no error)

Simple Variable

Property value:   "Hello {firstName}, welcome to apptor flow."
Variable context: { firstName: "Jane" }
Resolved: "Hello Jane, welcome to apptor flow."

Nested Object Access

Use dot notation to access nested objects:

{customer.name}
{customer.address.city}
{order.billing.amount}

If any segment in the path is null or missing, the entire expression resolves to an empty string.

Environment Variable Access

Secrets and environment configuration are accessed via the env prefix:

{env.OPENAI_API_KEY}
{env.DATABASE_URL}
{env.MY_SECRET_NAME}

The env namespace reads from:

  1. Secrets configured in the organization's secret store
  2. System environment variables (if permitted)

Multiple Variables in One String

"Order {orderId} placed by {customer.name} on {orderDate} — total: {currency}{amount}"

All occurrences are resolved in a single pass.


JUEL Expressions — ${expression}

Resolved by the Java Unified Expression Language (JUEL) engine. Only used in:

  • condition.expression in If/Else nodes
  • Loop condition expressions in Loop nodes

Comparison Operators

${status == 'approved'}
${amount != 0}
${count > 10}
${score >= 90}
${priority < 3}
${retries <= 5}

Boolean Operators

${status == 'active' && role == 'admin'}
${type == 'error' || type == 'critical'}
${!isCompleted}

String Operations

${name.startsWith('APPTOR')}
${email.endsWith('@apptor.io')}
${message.contains('urgent')}
${name.toLowerCase() == 'admin'}

Null Checks

${userId != null}
${result == null}
${data.items != null && data.items.size() > 0}

Numeric Operations

${amount * 1.2 > 1000}
${score / totalPossible >= 0.8}
${items.size() > 0}

Accessing Variables in JUEL

In JUEL expressions, variables are accessed directly by name (no curly braces):

${customerName == 'John'}         ← direct variable access
${order.amount > 100} ← nested access
${env.FEATURE_FLAG == 'enabled'} ← env variable

Variable Scope

Variables are scoped to the process instance (a single workflow execution). Different executions have completely isolated variable contexts.

Variable Sources

SourceHow
Workflow trigger inputVariables passed in POST /process/exec body
Node outputEach node can declare outputVariables
Set Variable nodeExplicitly sets named variables
Input NodeForm submission data becomes variables
Script TaskScript can write variables to context
AI TaskLLM response written to configured output variable
Environment{env.NAME} reads secrets and config

Subprocess Variable Scope

Subprocesses have their own variable scope. Variables are explicitly passed in via inputVariables mapping and out via outputVariables mapping. The child cannot automatically access the parent's variables unless explicitly mapped.


Common Mistakes

1. Using ${...} in a node property

# AI Task systemPrompt — WRONG:
"Hello ${customerName}, your order ${orderId} is ready"
→ The literal text "${customerName}" appears in the AI prompt

# Correct:
"Hello {customerName}, your order {orderId} is ready"

2. Using {...} in a condition expression

# If/Else condition — WRONG:
{status == 'approved'}
→ Not evaluated as a condition; always routes to false

# Correct:
${status == 'approved'}

3. Misspelling a variable name

Template variable resolution is silent on missing variables — it returns an empty string. If your email subject shows up as "Dear , your order is ready" then the variable name is wrong.

Use the Execution Console to inspect variable state at each node and verify names.

4. Case mismatch

{CustomerName}{customerName}. Both JUEL expressions and template variables are case-sensitive.

5. Wrong access path for nested objects

# Object: { user: { profile: { name: "Jane" } } }
{user.profile.name} ← correct
{user.name} ← wrong — returns empty string