Skip to main content

Variables Reference

Complete reference for variable syntax, scope, and resolution in apptor flow workflows.


Two Syntaxes — Never Mix Them

There are exactly two variable syntaxes in apptor flow. Using the wrong one is the most common mistake.

ContextSyntaxResolved ByExample
Node property values (prompts, bodies, addresses, etc.){varName}TemplateVariableResolverHello {customerName}
JUEL condition expressions (If/Else, Loop conditions)${expression}JUEL engine${total > 100}

{varName} is for data substitution. ${expression} is for boolean conditions.

They are completely separate systems and are never interchangeable.


{varName} — Template Variables

Basic Usage

Hello {customerName}, your order #{orderId} is ready.

The TemplateVariableResolver replaces {varName} with the current value of that variable at execution time.

Nested Object Access

{order.id}              → order['id']
{order.customer.email} → order['customer']['email']
{items[0].name} → items[0]['name']

Dot notation is supported for any depth. Array index access uses [n].

Environment / Secret Variables

{env.OPENAI_API_KEY}    → resolved from secrets store
{env.SUPPORT_EMAIL} → resolved from system variables

{env.VAR_NAME} reads from the organization's secrets store and system variables. These values are never logged.

Null / Missing Variables

If a variable is not found in the execution context:

  • The template resolver leaves the placeholder in place: Hello {unknownVar}Hello {unknownVar}
  • No error is thrown — this is a silent substitution failure
  • Use If/Else with ${empty varName} to guard against missing variables

Where {varName} Works

  • AI Task: systemPrompt, prompt
  • Service Task: endpointPath, body
  • Email: recipientAddress, subject, body
  • SMS: toNumber, message
  • Voice Task: toPhoneNumber, greeting, systemPrompt
  • Set Variable: value fields
  • Output Node: prompt, title
  • Input Node: formTitle
  • Script Task: variables are injected directly as named constants
  • Loop Node: loopExpression values (the {} part, not the ${} wrapper)

${expression} — JUEL Expressions

JUEL (Java Unified Expression Language) is used exclusively for condition evaluation — specifically:

  • If/Else node outgoing link conditions
  • Loop Node loopExpression

Syntax

${variableName == 'value'}
${numericVar > 100}
${booleanVar == 'true'}
${!empty someVar}
${var1 == 'x' && var2 == 'y'}

The entire expression must be wrapped in ${...}.

Operators

OperatorExampleNotes
==${status == 'active'}Equality (use single quotes for strings)
!=${type != 'billing'}Inequality
>, <${total > 500}Numeric comparison
>=, <=${count <= 10}Numeric comparison
&&${a == 'x' && b == 'y'}Logical AND
||${a == 'x' || b == 'y'}Logical OR
!${!active}Negation
empty${empty customerEmail}True if null, empty string, or empty collection

Type Coercion

All workflow variables are stored as strings internally. JUEL handles type coercion:

${total > 100}    → coerces 'total' to number if it looks like one
${flag == 'true'} → string comparison — CORRECT for boolean flags
${flag == true} → DO NOT USE — type mismatch, may behave unexpectedly
warning

Always compare boolean-like variables as strings: ${flag == 'true'} not ${flag == true}.

String Functions (EL)

${name.startsWith('J')}
${email.contains('@')}
${status.toLowerCase() == 'active'}
${name.length() > 3}
${name.trim().isEmpty()}

Variable Scope

Variables accumulate and persist across the entire workflow execution:

[Start: {customerName: 'Jane', orderId: 'ORD-123'}]

[AI Task] → sets: {classification: 'billing'}

[Service Task] → sets: {serviceTaskResponse: {...}, serviceTaskStatusCode: 200}

[Script Task] → sets: {formattedTotal: '$99.00'}

[End]

At End Event, the process instance data contains:

{
"customerName": "Jane",
"orderId": "ORD-123",
"classification": "billing",
"serviceTaskResponse": {...},
"serviceTaskStatusCode": 200,
"formattedTotal": "$99.00"
}

Variable Overwriting

Later nodes can overwrite variables set by earlier nodes. This is intentional and useful for updating values. Use unique variable names if you need to preserve intermediate values.


Special System Variables

The engine sets these automatically during execution:

VariableDescription
{_error}Error message from the most recent failed node
{_errorCode}Error code from the most recent failure
{loopIndex}Zero-based index in current loop iteration
{loopCount}Total completed iterations in current loop

Passing Variables Between Nodes

Service Task → Downstream

[Service Task: GET /customers/{customerId}]
outputs: {serviceTaskResponse}, {serviceTaskStatusCode}

[Script Task: extract fields]
script: return { customerName: serviceTaskResponse.name, customerEmail: serviceTaskResponse.email }

[Email: send email]
recipientAddress: {customerEmail}
subject: Hello {customerName}

AI Task → Downstream (Structured JSON)

[AI Task: classify ticket]
prompt: "Return JSON: {\"classification\": \"...\", \"priority\": \"...\"}"
outputs: {aiResponse} (raw), also sets {classification}, {priority} if JSON parsed

[If/Else]
trueFlow: ${classification == 'billing'}

Script Task → Downstream

// script:
return {
totalFormatted: `$${parseFloat(total).toFixed(2)}`,
isHighValue: parseFloat(total) > 1000
};
[Downstream If/Else]
trueFlow: ${isHighValue == 'true'}

Common Mistakes

Wrong: Using ${} in Property Fields

WRONG:  systemPrompt: "Hello ${customerName}"     ← JUEL syntax in template context
RIGHT: systemPrompt: "Hello {customerName}"

Wrong: Forgetting Single Quotes in JUEL

WRONG:  ${classification == billing}   ← unquoted string
RIGHT: ${classification == 'billing'}

Wrong: Comparing Booleans Without Quotes

WRONG:  ${isApproved == true}
RIGHT: ${isApproved == 'true'}

Wrong: Not Checking for Null

WRONG:  ${customerEmail == 'jane@example.com'}   ← throws if customerEmail is null
RIGHT: ${!empty customerEmail && customerEmail == 'jane@example.com'}