Skip to content

Using Input and Output Event Data

Understanding data flow through your automation is important for building effective Flows. This explains how to access, reference, and manipulate event data.

Events accumulate data as they flow through blocks. Each block adds its output to existing event data using the block’s camelCase name as the key.

Example flow: “Get User Name” → “Get User Role” → “Access Check”

Final event structure:

{
"getUserName": {
"name": "John Doe",
"email": "johndoe@example.org"
},
"getUserRole": {
"role": "admin"
},
"accessCheck": {
"allowed": true
}
}

Access outputs of previous blocks through the outputs variable:

outputs.getUserName.name // "John Doe"
outputs.getUserRole.role // "admin"
outputs.accessCheck.allowed // true
// Slack message
`Hello <@${outputs.getUserName.name}>! Your role is: ${outputs.getUserRole.role}`
// Conditional logic
outputs.accessCheck.allowed && outputs.getUserRole.role === 'admin'
// Dynamic URL
`https://api.example.com/users/${outputs.getUserName.email}/permissions`

While outputs gives you access to event data from previous blocks, the ref() function lets you reference signals from blocks with lifecycles (e.g. an S3 Bucket Resource)

ref("signal.blockName.signalName")

Example:

ref("signal.myS3Bucket.arn") // AWS S3 bucket ARN reference

Block names automatically convert to camelCase for event data keys. Numbers are included in the camelCase name, and content within parentheses is automatically excluded from the conversion.

Block NameData Key
”Get User Name”getUserName
”API Response”apiResponse
”Process Data 2”processData2
”Test Block (3)“testBlock
”Profile (Beta)“profile
`User: ${outputs.userLookup.name} (${outputs.userLookup.email})` // String interpolation
outputs.userLookup.name.toUpperCase() // Data transformation
outputs.userLookup.email.includes('@company.com') // String method check
outputs.calculation.result * 1.2 // Mathematical operations
outputs.metrics.count + outputs.metrics.errors // Addition
outputs.userList.users.length // Array length
outputs.userList.users.map(user => user.name) // Array transformation
outputs.userList.users.filter(user => user.active) // Array filtering
outputs.apiResponse.data.items[0].id // Array element access
outputs.configuration.settings.timeout // Property access
outputs.validation.isValid && outputs.permissions.hasAccess // Boolean operations
outputs.userProfile.role === 'admin' // Property check
!outputs.check.failed || outputs.retry.available // Fallback logic
`Display Name: ${outputs.getUserMetadata.user.display_name || 'Not set'}` // Default assignment
outputs.error.occurred ? `Error: ${outputs.error.message}` : `Success: Processed ${outputs.result.count} items` // Conditional logic
outputs.performance.responseTime > 1000 // Comparison

Use anonymous functions for multi-step processing:

(function() {
const users = outputs.userQuery.results || [];
const activeUsers = users.filter(user => user.status === 'active');
return {
total: activeUsers.length,
names: activeUsers.map(user => user.name),
lastLogin: Math.max(...activeUsers.map(user => new Date(user.lastLogin).getTime()))
};
})()

After connecting blocks, Spacelift shows input schemas so you know what data is available. Use the debug feature to send test events and verify your expressions work correctly.