Workflow Variables
Workflow variables store and pass data between tasks. They can be defined in the variables section of a workflow or task, and assigned in inputs, variables, and outputs sections.
Types of Variablesβ
System Variablesβ
Automatically created and accessed using double curly braces:
{{ workflowId }}: ID of the workflow{{ workflowName }}: Name of the workflow{{ organizationId }}: ID of the organization{{ currentUserId }}: ID of the current user{{ currentEmployeeId }}: ID of the current employee
User-Defined Variablesβ
Created and used by workflow designers.
Accessing Variablesβ
Basic Syntaxβ
Use double curly braces: {{ variableName }}
Nested Variablesβ
Use dot notation: {{ order.orderId }}
Optional Chainingβ
Use ?. to avoid errors with undefined properties:
inputs:
to: "{{order?.customer?.email?}}"
Working with List Variablesβ
List variables can be accessed using square brackets.
For example, to access the first item in the commodities array variable, use {{ commodities[0] }}.
Accessing List Itemβ
List variables can be accessed using square brackets.
For example, to access the first item in the commodities array variable, use {{ commodities[0] }}.
Filtering Listβ
Nested list can be filtered using square brackets. For example, to filter commodities by type, use {{ commodities[type="box"] }}.
This will return all commodities with a type of box.
Accessing Item in Filtered Listβ
Nested arrays can be accessed using square brackets. For example, to access the first item in the commodities array variable, use {{ commodities[type="box"].[0] }}.
This will return the first commodity with a type of box.
Flatten Listβ
To flat the list of orders in the jobs variable, use {{ jobs[*].orders }}.
This will return a list of orders.
Recursive Flattenβ
To recursively flatten a self-referencing structure (like nested commodities), use [**].
For example, if commodities can contain other commodities:
inputs:
allCommodities: "{{ order.commodities[**] }}"
This will return all commodities at all nesting levels in a single flat list.
Example structure:
Box A (level 1)
βββ Package 1 (level 2)
β βββ Item X (level 3)
β βββ Item Y (level 3)
βββ Package 2 (level 2)
βββ Item Z (level 3)
Using containerCommodities[**] returns: [Box A, Package 1, Item X, Item Y, Package 2, Item Z]
Depth Indexβ
Use [-N] to filter items by depth from the end of a self-referencing structure:
[-1]- Returns leaf nodes (items with no children)[-2]- Returns parents of leaf nodes (second-to-last level)[-3]- Returns grandparents of leaf nodes (third-to-last level)
Examples:
inputs:
# Get all leaf items (deepest level)
leafItems: "{{ order.commodities[**][-1] }}"
# Get all packages (parents of leaf items)
packages: "{{ order.commodities[**][-2] }}"
# Get all boxes (grandparents of leaf items)
boxes: "{{ order.commodities[**][-3] }}"
Example with variable depths:
If you have commodities with varying nesting depths (some 3 levels, some 4 levels), [-2] will always return the parents of the deepest items in each branch:
Box A β Package 1 β Item X (3 levels)
Box B β Package 2 β Subpackage β Item Y (4 levels)
Using [**][-2] returns: [Package 1, Subpackage] (parents of leaves in each branch)
inputs:
leafItems: "{{ order.commodities.containerCommodities[**][-1] }}"
packages: "{{ order.commodities.containerCommodities[**][-2] }}"
boxes: "{{ order.commodities.containerCommodities[**][-3] }}"
Field Selectionβ
To select a specific field from a list of orders, use {{ jobs[*].orders.[*].{ id customerId parentJobId:_.jobId } }}.
- To access parent object fields, use
_.fieldName. - To use aliases, use
alias:fieldName.
This will return a list of orders with the following fields: id, customerId and parentJobId.
[
{
"id": "1",
"customerId": "1",
"parentJobId": "1"
},
{
"id": "2",
"customerId": "2",
"parentJobId": "1"
}
]
Sub Expressionsβ
Sub Expressions can be used to access nested variables. Subexpressions are surrounded by parentheses.
For example {{order.items.[status=(selected_status)].[0].name}}.
Sub Expressions are useful when you need to map specific reference data based on the value of another variable.
Sub Expression Exampleβ
The following example shows how to map a state code based on the state name.
inputs:
address:
stateCode: "{{states.[name=(address.stateName)].code}}"
Converting Variablesβ
Variables can be converted to a specific type using the following functions:
string- converts a variable to a stringint- converts a string to an integerdecimal- converts a string to a decimalbool- converts a string to a booleandatetime- converts a string to a datetimeluceneString- converts a string to a lucene stringemptyIfNull- returns empty string if the variable is nullnullIfEmpty- returns null if the variable is emptytransliterate- converts a cyrillic string to a latin stringparseJson- parses a json stringtoJson- converts a variable to a json string
inputs:
orderId: "{{ string orderId }}"
Transliterateβ
Transliterate is used to convert a cyrillic string to a latin string.
Example of Transliterate:
inputs:
name: "{{ transliterate name }}" # transliterate name ΠΡΠΈΠ²ΡΡ, ΡΠΊ ΡΠΏΡΠ°Π²ΠΈ?"; will be converted to "Privit, yak spravi?"
toJsonβ
toJson is used to convert a variable to a json string.
Example of toJson:
inputs:
orderJson: "{{ toJson order }}"
parseJsonβ
parseJson is used to parse a json string.
Example of parseJson:
inputs:
order: "{{ parseJson orderJson }}"
Workflow-Level Variable Sourcesβ
Workflow-level variables (defined in the top-level variables section) support three resolution strategies:
Static Value / Templateβ
The default. Assigns a static value or template expression:
variables:
- name: "organizationId"
value: "{{ Order.OrganizationId }}"
- name: "threshold"
value: 100
From Configurationβ
Loads the value from an organization configuration record:
variables:
- name: "apiKey"
fromConfig:
configName: "apps.novaposhta"
key: "apiKey"
- name: "allSettings"
fromConfig:
configName: "apps.novaposhta"
# Omit key to get the entire config object
NCalc Expressionβ
Evaluates an NCalc expression at runtime. The expression has access to all previously resolved variables:
variables:
- name: "baseRate"
value: 10
- name: "adjustedRate"
expression: "[baseRate] * 1.15"
- name: "isHighValue"
expression: "[orderTotal] > 5000"
Variables are resolved in order, so a variable using expression can reference variables defined earlier in the list.
Complex Variablesβ
Extendsβ
Extends are used to extend the inputs of an activity. The inputs of an activity can be extended using the following syntax:
inputs:
order:
extends: "purchaseOrder"
defaultIfNull: []
mapping: # override or extend the order input
customerId: "{{ customerId }}"
Expressionβ
Initializes a variable using an expression.
inputs:
orderDate:
expression: "now()"
Foreach collection itemβ
Foreach collection item is used to iterate over a collection of items and construct a new collection of items.
Example of ForEach variables section:
activities:
- name: "UpdateCommodityStatus"
description: "Update the status of a commodity for shipped orders."
steps:
- task: "UpdateCommodityStatus"
inputs:
commodities:
foreach: "order.commodities"
item: "item"
mapping: # object mapping
commodityId: "{{item.id}}"
status: "Shipped"
commoditiesIds:
foreach: "order.commodities"
item: "item"
mapping: "{{item.id}}" # simple array mapping
Switch Casesβ
Switch cases are used to define a mapping between a value and a result.
Example of Switch Cases:
inputs:
status:
switch: "{{ statusName }}"
cases:
"Shipped": 1
"Delivered": 2
"Returned": 3
default: 0
Coalesceβ
Coalesce is used to return the first non-null value.
Example of Coalesce:
inputs:
status:
coalesce:
- "{{ order.status }}"
- "{{ order.statusName }}"
- "Pending"
Encrypt and Decryptβ
Encrypt and Decrypt are used to encrypt and decrypt sensitive data.
Example of Encrypt and Decrypt:
variables:
creditCard:
encrypt:
value: "{{ creditCard }}"
keyName: "stripe" # key name is used to lookup the encryption key
inputs:
creditCard:
number:
decrypt:
encryptedValue: "{{ creditCard.number }}"
keyName: "stripe" # key name is used to lookup the encryption key
Resolveβ
Resolve performs an entity lookup by querying a GraphQL collection and returning a specific field (typically the entity ID). This is useful when you have a human-readable identifier (like a name or code) and need to resolve it to a database ID.
The resolve directive uses a pre-processor that batches and caches all resolve requests before the workflow step executes, preventing N+1 query issues.
inputs:
customerId:
resolve:
entity: "Contact" # Entity type to query (pluralized automatically: contacts)
filter: "name={{ customerName }}" # Lucene filter expression
field: "contactId" # Field to return (default: <entity>Id, e.g., contactId)
Properties:
| Property | Type | Required | Description |
|---|---|---|---|
entity | string | Yes | Entity type name (e.g., Contact, PackageType). Automatically pluralized for the GraphQL query. |
filter | string | Yes | Lucene filter expression to find the entity. Supports template variables. |
field | string | No | Field to return from the matched entity. Defaults to <entity>Id (e.g., contactId for Contact). |
Example β Resolving a package type by name:
inputs:
commodities:
foreach: "importedCommodities"
item: "item"
mapping:
packageTypeId:
resolve:
entity: "PackageType"
filter: "name={{ item.packageTypeName }}"
weight: "{{ item.weight }}"
description: "{{ item.description }}"
Resolve results are cached per unique combination of entity + filter + field. When multiple items in a foreach reference the same entity (e.g., the same package type name), only one GraphQL query is executed.
$raw and $evalβ
$raw and $eval are used to define raw and evaluated expressions.
Example of $raw and $eval:
inputs:
order:
$raw: |
{
"id": 1,
"customerId": 1
}
order:
$eval: |
{
"id": "{{ orderId }}",
"customerId": "{{ customerId }}"
}
Raw will use string as is and eval will parse the string as JSON object.