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
| Context | Syntax | Engine | Where |
|---|---|---|---|
| Node property values | {varName} | TemplateVariableResolver | systemPrompt, prompt, recipientAddress, subject, body, script, any text property |
| Condition expressions | ${expression} | JUEL engine | condition.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:
- Secrets configured in the organization's secret store
- 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.expressionin 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
| Source | How |
|---|---|
| Workflow trigger input | Variables passed in POST /process/exec body |
| Node output | Each node can declare outputVariables |
| Set Variable node | Explicitly sets named variables |
| Input Node | Form submission data becomes variables |
| Script Task | Script can write variables to context |
| AI Task | LLM 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