One of the most powerful features in Activepieces is the ability to pass data between steps. This guide explains how to reference step outputs, use expressions, and map data throughout your workflow.
Understanding Step Outputs
Every step in your workflow produces an output that subsequent steps can access. The output structure depends on the step type.
Accessing Step Outputs
Use the mustache syntax {{ stepName.property }} to reference data from previous steps:
// If you have a step named "get_user" that returns:
{
"id" : "123" ,
"name" : "John Doe" ,
"email" : "john@example.com"
}
// Access the data in later steps:
{{ get_user . name }} // "John Doe"
{{ get_user . email }} // "john@example.com"
{{ get_user . id }} // "123"
Step names are automatically generated as step_1, step_2, etc., but you can rename them to something more meaningful like get_user or send_email.
Variable Types
Activepieces supports several types of variables you can use in your workflows:
1. Step Outputs
Access data from any previous step:
{{ trigger . body . name }} // Data from the trigger
{{ send_http . body . results }} // HTTP response data
{{ transform_data . output }} // Code action output
2. Loop Variables
When inside a loop, access iteration data:
// From packages/server/engine/test/handler/flow-looping.test.ts
{
"type" : "LOOP_ON_ITEMS" ,
"settings" : {
"items" : "{{ [4, 5, 6] }}" // Array to iterate over
},
"firstLoopAction" : {
"name" : "process_item" ,
"settings" : {
"input" : {
"currentItem" : "{{ loop.item }}" , // Current item
"index" : "{{ loop.index }}" , // Current index (1-based)
"iteration" : "{{ loop.iterations }}" // All iterations
}
}
}
}
loop.item The current item being processed
loop.index Current iteration number (1-based)
loop.iterations Array of all completed iterations
3. Connections
Reference saved connection credentials:
{{ connections . my_database . host }}
{{ connections . my_database . username }}
4. Router/Branch Data
Access branch evaluation results:
// Router output from packages/server/engine/test/handler/flow-branching.test.ts
{
"router" : {
"output" : {
"branches" : [
{
"branchIndex" : 1 ,
"branchName" : "Test Branch" ,
"evaluation" : true // Condition result
}
]
}
}
}
Using Expressions
Activepieces supports JavaScript expressions within the mustache syntax:
String Operations
// Concatenation
{{ trigger . firstName + " " + trigger . lastName }}
// Template literals
{{ `Hello, ${ trigger . name } !` }}
// String methods
{{ trigger . email . toLowerCase () }}
{{ trigger . name . toUpperCase () }}
{{ trigger . text . substring ( 0 , 10 ) }}
Number Operations
// Arithmetic
{{ trigger . price * 1.1 }} // Add 10%
{{ trigger . quantity + 5 }}
{{ ( trigger . total - trigger . discount ). toFixed ( 2 ) }}
// Comparisons
{{ trigger . age >= 18 }}
{{ trigger . score > 100 }}
Array Operations
// Map, filter, and more
{{ trigger . items . map ( item => item . name ) }}
{{ trigger . users . filter ( user => user . active ) }}
{{ trigger . numbers . reduce (( sum , n ) => sum + n , 0 ) }}
// Array access
{{ trigger . items [ 0 ] }} // First item
{{ trigger . items . length }} // Array length
Object Operations
// Access nested properties
{{ trigger . user . profile . address . city }}
// Spread operator
{{ { ... trigger . user , updated : true } }}
// Object methods
{{ Object . keys ( trigger . data ) }}
{{ Object . values ( trigger . metadata ) }}
Data Mapping Examples
HTTP Request Step
A step named fetch_users makes an API call: {
"status" : 200 ,
"body" : {
"users" : [
{ "id" : 1 , "name" : "Alice" , "active" : true },
{ "id" : 2 , "name" : "Bob" , "active" : false },
{ "id" : 3 , "name" : "Charlie" , "active" : true }
]
}
}
Filter Active Users
Create a code action to filter: export const code = async ( inputs ) => {
const users = inputs . allUsers ;
return users . filter ( user => user . active );
};
Input configuration: {
"allUsers" : "{{ fetch_users.body.users }}"
}
Use Filtered Data
Reference the filtered results: {{ filter_users . output }} // [{id: 1, name: "Alice", ...}, ...]
Example 2: Loop Over Items
// Loop configuration from test suite
{
"name" : "process_orders" ,
"type" : "LOOP_ON_ITEMS" ,
"settings" : {
"items" : "{{ fetch_orders.body.results }}" // Array from previous step
},
"firstLoopAction" : {
"name" : "send_confirmation" ,
"type" : "PIECE" ,
"settings" : {
"pieceName" : "@activepieces/piece-gmail" ,
"actionName" : "send_email" ,
"input" : {
"to" : "{{ loop.item.customer.email }}" ,
"subject" : "Order #{{ loop.item.orderNumber }} Confirmed" ,
"body" : "Thank you for order #{{ loop.index }}!"
}
}
}
}
Example 3: Conditional Data
Use ternary operators for conditional values:
// Set priority based on score
{{ trigger . score > 80 ? 'High' : trigger . score > 50 ? 'Medium' : 'Low' }}
// Default values
{{ trigger . name || 'Unknown' }}
{{ trigger . count ?? 0 }}
Testing Data Flow
When you test a workflow, you can inspect data at each step:
Test Flow
View Step Output
Debug Issues
Click Test Flow to run your workflow with sample data. // From packages/web/src/app/builder/flow-canvas/widgets/test-flow-widget.tsx
const { mutate : runFlow } = flowHooks . useTestFlowOrStartManualTrigger ({
flowVersionId: flowVersion . id ,
onUpdateRun : ( response ) => {
// Each step's output is captured
const steps = response . flowRun . steps ;
// { step_1: {output: ...}, step_2: {output: ...} }
}
});
Click on any executed step to see:
Input values that were used
Output data that was produced
Execution time and status
// Step output structure
{
"status" : "SUCCEEDED" ,
"output" : {
// Actual step output data
},
"input" : {
// Input values used
},
"duration" : 1250 // milliseconds
}
If data isn’t flowing correctly:
Check step names are correct
Verify the step executed successfully
Inspect the actual output structure
Ensure property paths are correct
Common mistake: Using {{ trigger.name }} when the trigger output is {{ trigger.body.name }}
Sample Data
Steps can save sample data for testing:
// From packages/shared/src/lib/automation/flows/actions/action.ts
export const SampleDataSetting = {
"sampleDataFileId" : string , // Saved sample output
"sampleDataInputFileId" : string , // Saved sample input
"lastTestDate" : string , // When last tested
"currentSelectedData" : object // Current test data
}
Sample data is automatically saved when you test individual steps, making it easy to test downstream steps without re-running the entire flow.
Advanced Data Techniques
Working with JSON
// Parse JSON string
{{ JSON . parse ( trigger . jsonString ) }}
// Stringify object
{{ JSON . stringify ( trigger . data ) }}
// Pretty print
{{ JSON . stringify ( trigger . data , null , 2 ) }}
Date and Time
// Current timestamp
{{ Date . now () }}
// Format date
{{ new Date ( trigger . timestamp ). toISOString () }}
// Date arithmetic
{{ new Date ( Date . now () + 86400000 ). toISOString () }} // Tomorrow
Error Handling in Expressions
// Safe property access
{{ trigger ?. user ?. profile ?. email || 'no-email@example.com' }}
// Try-catch in code actions
export const code = async ( inputs ) => {
try {
return JSON . parse ( inputs . data );
} catch ( error ) {
return { error: error . message };
}
};
Common Patterns
Combine data from different steps: {
"userData" : "{{ get_user.output }}" ,
"orderData" : "{{ get_orders.body }}" ,
"timestamp" : "{{ Date.now() }}"
}
Construct URLs from variables: {{ `https://api.example.com/users/ ${ trigger . userId } /orders?limit=10` }}
Conditional Field Inclusion
Include fields only if they exist: {
"name" : "{{ trigger.name }}" ,
... {{ trigger . email ? { email: trigger . email } : {} }}
}
Best Practices
Use Meaningful Step Names : Rename steps to reflect what they do (e.g., get_user instead of step_1)
Test Data Flow : Always test with real data to verify expressions work correctly
Handle Missing Data : Use null coalescing (??) or defaults for optional fields
Keep Expressions Simple : Complex logic should go in code actions
Document Complex Mappings : Add notes explaining non-obvious data transformations
Next Steps
Loops & Branches Learn control flow and conditional logic
Code Actions Write custom data transformations
Debugging Debug data flow issues
Error Handling Handle data errors gracefully