Working with Variables
Variables are essential for passing data between steps in ProActions workflows. This guide explains how to use variables, the flow context, and dynamic data in your flows.
The Flow Context
The flow context is a data store that persists throughout the execution of a flow. Each step can:
- Read variables from the flow context
- Write new variables to the flow context
- Modify existing variables
Think of it as a shared workspace where steps exchange data.
Variable Syntax: Two Types
ProActions uses two different variable syntaxes depending on what you're accessing:
Built-in Variables (Single Braces)
Built-in variables provided by ProActions use single curly braces {}:
- step: HUB_COMPLETION
instruction: "Summarize: {textContent}"
Custom Variables (Double Braces with flowContext)
Custom variables stored in the flow context use double curly braces with flowContext prefix:
- step: SET
myVariable: "Hello World"
- step: SHOW_NOTIFICATION
message: "Value: {{ flowContext.myVariable }}"
Scoped Reusable Namespaces
When using reusable blocks (BLOCKS, section.blocks, action.blocks, and imports), template resolution also exposes scoped namespaces:
global- reusable blocks defined inAI_KIT.BLOCKSsection- reusable blocks defined insection.blockslocal- reusable blocks defined inaction.blocksimports.<alias>- reusable blocks from imported modules
Scoped Constants and Vars
AI_KIT:
BLOCKS:
constants:
modulesBasePath: /SysConfig/ProActions/lib
SECTIONS:
- section: Editorial
blocks:
vars:
endpoint: '{{ global.constants.modulesBasePath }}/api'
actions:
- title: Show Endpoint
flow:
- step: SHOW_NOTIFICATION
message: '{{ section.vars.endpoint }}'
Scoped References in Flow and Script Reuse
- step: CALL_FLOW
ref: '{{ section.vars.defaultFlowRef }}'
- step: SCRIPTING
scriptRef: local.scripts.normalizeTitle
returnAs: normalizedTitle
Lazy Vars Behavior
Scoped vars are evaluated lazily at access time. This means they can reference current flowContext values, and changes in flowContext are reflected when the var is resolved later in the flow.
Built-in Variables
ProActions automatically provides several built-in variables based on the current context:
Document Variables
{textContent}- Full text content of the current document{xmlContent}- Full XML content of the current document{selectedText}- Currently selected text in the editor{selectedXml}- XML of the current selection{paragraphText}- The text of the paragraph in which the cursor is located.{paragraphXML}- The XML of the paragraph in which the cursor is located.{documentId}- The ID of the currently opened document{documentLoid}- The LOID of the currently opened document{documentUuid}- The UUID of the currently opened document{documentName}- The name of the currently opened document{issueDate}- The issue date of the currently opened document{reportId}- The id of the currently opened report{reportLoid}- The LOID of the currently opened report{reportUuid}- The UUID of the currently opened report{prompt}- The current prompt text (for prompt-related operations)
System Variables
{BASE_URL}- The base URL of your Swing/Prime instance{userName}- Current logged-in user's username{userId}- Current user's ID{appName}- prime or swing{edapiUrl}- The baseUrl of Swing's EDAPI{edapiToken}- The user's EDAPI token
Example Usage
- step: HUB_COMPLETION
behavior: "You are a helpful assistant."
instruction: "Summarize this text: {textContent}"
Setting Variables with SET
The SET step is the primary way to create and assign variables:
Simple Assignment
- step: SET
myVariable: "Hello World"
anotherVar: 42
isActive: true
Using Expressions
You can combine built-in and custom variables:
- step: SET
greeting: "Hello, {userName}!"
fullMessage: "{{ flowContext.greeting }} Welcome back!"
Using Template Syntax
Access client functions and perform complex operations:
- step: SET
text: '{{ client.getTextContent({ excludeTags: ["caption", "credit"] }) | safe }}'
filename: "{{ client.getMetadata().metadata.general.title }}.mp3"
Executing Steps to Set Variables
- step: SET
name: headline
steps:
- step: GET_TEXT_CONTENT
at: XPATH
xpath: "/doc/story/headline/p"
Reading Variables
From Built-in Sources
- step: SHOW_NOTIFICATION
message: "Selected text: {selectedText}"
From Flow Context
- step: SET
userName: "John Doe"
- step: SHOW_NOTIFICATION
message: "User: {{ flowContext.userName }}"
Nested Properties
Access object properties using dot notation:
- step: SET
myObject:
attrib1: "content"
- step: SHOW_NOTIFICATION
message: "Object name: {{ flowContext.myObject.attrib1 }}"
notificationType: success
Array Access
Access array elements by index:
- step: SET
myArray: ["asd", "qwe"]
- step: SHOW_NOTIFICATION
message: "Object name: {{ flowContext.myArray[0] }}"
notificationType: success
How Steps Pass Data
Steps automatically store their results in the flow context. The specific variable names depend on each step's implementation:
Example: Automatic Data Flow
# GET_TEXT_CONTENT stores result in flow context
- step: GET_TEXT_CONTENT
at: CURSOR
# HUB_COMPLETION can read text (from previous step)
# and stores its result for the next step
- step: HUB_COMPLETION
instruction: "Improve: {{ flowContext.text }}"
# REPLACE_TEXT uses the completion result automatically
- step: REPLACE_TEXT
at: CURSOR
Example: Named Outputs
Some steps support custom output names via outputs:
- step: SELECTED_OBJECT
outputs:
- type: object
name: myObject
- step: SHOW_NOTIFICATION
message: "ID: {{ flowContext.myObject.id }}"
Conditional Variables
Use variables in conditional statements:
Simple Conditions
- step: IF
condition: "{{ client.isTextSelected() }}"
then:
- step: SHOW_NOTIFICATION
message: "You have selected text"
Comparison Operators
- step: SET
wordCount: 150
- step: IF
condition: "{{ flowContext.wordCount > 100 }} "
then:
- step: SHOW_NOTIFICATION
message: "Long article detected"
Logical Operators
- step: IF
condition: "{{ client.isTextSelected() && flowContext.otherVar }}"
then:
- step: HUB_COMPLETION
instruction: "Improve: {selectedText}"
Working with Lists
Creating Lists
- step: SET
myList:
- "Item 1"
- "Item 2"
- "Item 3"
Iterating Over Lists
- step: FOR
items: "{{ flowContext.myList }}"
var: currentItem
do:
- step: SHOW_NOTIFICATION
message: "Processing: {{ flowContext.currentItem }}"
List from AI Response
- step: HUB_COMPLETION
instruction: "Generate 5 topic ideas about {selectedText}"
response_format: "list"
- step: USER_SELECT
promptText: "Choose a topic:"
Working with Objects
Creating Objects
- step: SET
userInfo:
name: "{{ client.getUserName() }}"
role: "editor"
Accessing Object Properties
- step: SHOW_NOTIFICATION
message: "User {{ flowContext.userInfo.name }} is an {{ flowContext.userInfo.role }}"
Parsing JSON
- step: SET
text: '{"title": "My Article", "author": "John Doe"}'
- step: PARSE_JSON
- step: SHOW_NOTIFICATION
message: "Article by {{ flowContext.object.author }}"
Advanced: Scripting with Variables
For complex logic, use the SCRIPTING step:
JavaScript Expressions
- step: SCRIPTING
template: |
flowContext.wordCount = flowContext.textContent.split(' ').length;
flowContext.readingTime = Math.ceil(flowContext.wordCount / 200);
return flowContext; // always return the flowContext !
Script with Multiple Operations
- step: SCRIPTING
script: |
const text = client.getSelectedText() || '';
const words = text.split(' ');
const uniqueWords = [...new Set(words)];
flowContext.wordCount = words.length;
flowContext.uniqueWordCount = uniqueWords.length;
return flowContext;
Template Filters and Functions
The template syntax supports filters and client functions:
Common Filters
- step: SET
# String manipulation
uppercased: "{{ flowContext.text | upper }}"
truncated: "{{ flowContext.longText | truncate(50) }}"
# Safe output (no escaping)
content: "{{ client.getTextContent() | safe }}"
# Regex replacement
cleaned: "{{ flowContext.filename | replace(r/[\\/:?]/g, '_') }}"
Custom Resolver Filters
Use these filters when you need to traverse arrays/objects inside flowContext data:
- step: SET
users:
- id: 1
profile:
name: "Ada"
- id: 2
profile:
name: "Linus"
# Chain find + getAttr for targeted lookup
selectedName: "{{ flowContext.users | find('id', 2) | getAttr('profile') | getAttr('name') | default('Unknown') }}"
# Collect one nested path from all items
namesCsv: "{{ flowContext.users | pluck('profile.name') | join(', ') }}"
Examples below use standard Nunjucks filter syntax.
| Filter | Description | Example Usage |
|---|---|---|
getAttr(key) | Returns one own property by key from an object. Returns empty output when missing. | `{{ flowContext.user |
find(key, value) | Returns the first array item where own property key strictly equals value. | `{{ flowContext.users |
pluck(path) | Returns an array with values resolved from a dot path for each item. | `{{ flowContext.users |
includes(search) | Returns true if an array contains search or a string contains search. | `{{ flowContext.tags |
unique | Returns a de-duplicated array, preserving first-seen order. | `{{ flowContext.terms |
flatten | Recursively flattens nested arrays into a single-level array. | `{{ flowContext.nestedItems |
chunk(size) | Splits an array into chunks of size (invalid size returns empty array). | `{{ flowContext.items |
keys | Returns own enumerable property names. | `{{ flowContext.user |
values | Returns own enumerable property values. | `{{ flowContext.user |
entries | Returns own enumerable key-value pairs. | `{{ flowContext.user |
pick(...keys) | Returns a new object with only the requested own properties. | `{{ flowContext.user |
isString | Returns true when value is a string. | `{{ flowContext.title |
isNumber | Returns true when value is a number. | `{{ flowContext.score |
isObject | Returns true when value is a non-null object (arrays included). | `{{ flowContext.payload |
isNull | Returns true only when value === null. | `{{ flowContext.optionalField |
contains(fragment) | Returns true when text contains fragment. | `{{ flowContext.title |
startsWith(prefix) | Returns true when text starts with prefix. | `{{ flowContext.slug |
endsWith(suffix) | Returns true when text ends with suffix. | `{{ flowContext.filename |
split(separator, limit?) | Splits text by separator (string or regex). | `{{ flowContext.csv |
padStart(length, pad?) | Left-pads text to the target length. | `{{ flowContext.sequence |
match(pattern, flags?) | Returns regex match result or null for invalid input/pattern. | `{{ flowContext.text |
json | Returns JSON.stringify(value) output, or empty string if serialization fails. | `{{ flowContext.payload |
toInt | Converts to integer with fallback 0 when conversion fails. | `{{ flowContext.countText |
toFloat | Converts to float with fallback 0 when conversion fails. | `{{ flowContext.scoreText |
Client Functions
Access Swing/Prime client methods:
- step: SET
# Get metadata
title: "{{ client.getMetadata().metadata.general.title }}"
# Get document info
workFolder: "{{ client.getDocumentWorkfolder() }}"
# Get selected element
element: "{{ client.getSelectedElement() }}"
# Get text content with options
mainText: '{{ client.getTextContent({ excludeTags: ["caption"] }) | safe }}'
## Best Practices
1. Use Descriptive Variable Names
# Good
- step: SET
selectedArticleText: "{{ client.getSelectedText() }}"
# Avoid
- step: SET
txt: "{{ client.getSelectedText() }}"
2. Check Variable Existence
- step: IF
condition: "{{ flowContext.myText }}"
then:
- step: HUB_COMPLETION
instruction: "Process: {{ flowContext.myText }}"
else:
- step: SHOW_NOTIFICATION
message: "Please select text first"
3. Use Correct Syntax for Variable Type
# Built-in variables - single braces
- step: HUB_COMPLETION
instruction: "Process: {textContent}"
# Custom variables - double braces with flowContext
- step: SHOW_NOTIFICATION
message: "Result: {{ flowContext.myCustomVariable }}"
4. Avoid Overwriting Built-in Variables
# Be careful not to overwrite built-in variables
- step: SET
textContent: "My custom text" # This could cause confusion!
Debugging Variables
Use the DEBUG step to inspect the flow context:
- step: DEBUG
# This will log all variables to the browser console
Or show specific variables:
- step: SHOW_NOTIFICATION
message: "Debug: myVar = {{ flowContext.myVar }}"
Common Patterns
Pattern 1: Conditional Processing
- step: IF
condition: "{{ client.isTextSelected() }}"
then:
- step: HUB_COMPLETION
instruction: "Improve: {selectedText}"
- step: REPLACE_TEXT
at: CURSOR
else:
- step: SHOW_NOTIFICATION
message: "Please select text first"
Pattern 2: Multi-step Processing
- step: HUB_COMPLETION
instruction: "Generate article ideas about {selectedText}"
response_format: "list"
- step: USER_SELECT
promptText: "Choose an idea:"
- step: HUB_COMPLETION
instruction: "Write a full article about the selected topic"
- step: INSERT_TEXT
at: CURSOR
Next Steps
- Learn about Using Services to work with external APIs
- Review the Step Library to see all variable options
- Check out Configuration Basics for advanced patterns