Docs / Strand / templating/passing-data

Passing Data Between Steps

Learn how to pass data from one workflow step to another.

The Node Inspector's Data access reference, listing the template expressions for reaching a node's output, the initial payload, and vault secrets. Every node's inspector carries a Data access reference with copy buttons — the quickest way to get the exact expression for the value you want.

Key Concept

Each node in your workflow produces output events. Subsequent nodes can access this output using Jinja2 templating with JSONPath support.

All node types now support the same powerful templating features:

  • Full Jinja2 expressions and filters
  • JSONPath queries for complex data extraction
  • Access to steps, variables, and vault values
  • Consistent syntax across all nodes

Automatic Data Passing

When nodes are directly connected, data is automatically passed from one node to the next. You can access the previous node's output using payload:

jinja

{{ payload.field_name }}
{{ payload.email }}
{{ payload.user_id }}
Simple and Direct

For directly connected nodes, use payload - no need to reference the node ID!

Example: Direct Connection

If Node A outputs {"email": "[email protected]"} and connects to Node B:

In Node B's configuration, you can use:

jinja

    {{ payload.email }}

::: Instead of:

jinja

    {{ steps.node_A.output_payload.email }}

Accessing Non-Direct Nodes

When you need to access data from a node that's not directly connected, use step references:

Step References

Node ID

Every node has a unique ID. You can see it in the Node Inspector when you select a node.

Finding Node IDs
  1. Click on a node in your workflow
  2. Look at the Node Inspector panel on the right
  3. The Node ID is displayed at the top

Node ID Format in Templates

Node IDs are short alphanumeric strings (e.g., node_a1b2c3d4) that work directly in Jinja2 dot notation:

jinja

{{ steps.node_a1b2c3d4.output_payload.field_name }}
Legacy Node IDs

Older workflows may have node IDs with hyphens. For those, use bracket notation: {{ steps['node-1234567890'].output_payload }}. Hyphens in Jinja2 are interpreted as subtraction.

Accessing Output from Any Step

jinja

{{ steps.{node_id}.output_payload.field_name }}
Example: Accessing Non-Direct Node

If you have a workflow like: Node A -> Node B -> Node C

In Node C, to access Node A's output (not directly connected): :::

jinja

    {{ steps.node_A.output_payload.email }}
    {{ steps.node_A.output_payload.user_id }}

For example, if the node ID is node_a1b2c3d4: :::

jinja

    {{ steps.node_a1b2c3d4.output_payload.email }}
    {{ steps.node_a1b2c3d4.output_payload.user_id }}

::: But to access Node B's output (directly connected), just use: :::

jinja

    {{ payload.email }}

Available Properties

Property Description
steps.{node_id}.output_payload The payload (single output event)
steps.{node_id}.output_meta The metadata from the output
steps.{node_id}.output Full output array (all events)
steps.{node_id}.output_count Number of output events
Single vs Multiple Outputs

Most nodes produce a single output, so output_payload is usually what you want. If a node can produce multiple outputs, use output to access the array.

Common Patterns

Pattern 1: Lookup and Use (Direct Connection)

mermaid

graph LR
    A[HTTP Request: user_lookup] --> B[HTTP Request: send_email]

Configuration for Node B:

json

  {
    "to": "{{ payload.email }}"
  }
Why `payload` works

Since Node B is directly connected to Node A, Node A's output becomes Node B's input, accessible via payload.

Pattern 2: Transform and Pass (Direct Connection)

mermaid

graph LR
    A[Transform: user_transform] --> B[HTTP Request: update_profile]

Configuration for Node B:

Direct Connection

Since Node B is directly connected to the Transform node, use payload instead of steps.user_transform.output_payload.

Pattern 3: Filter and Process (Direct Connection)

mermaid

graph LR
    A[Filter: active_users] --> B[Process Active Users]

Configuration for Node B:

Filter Nodes

Filter nodes pass through data that matches the condition, so the next node receives that data as payload.

When to Use payload vs steps.{node_id}.output_payload

Use payload for Direct Connections

When a node is directly connected to the previous node, use payload:

mermaid

graph LR
    A[Node A] --> B[Node B]

In Node B's configuration:

jinja

{{ payload.field_name }}

Use steps.{node_id}.output_payload for Non-Direct Access

When you need to access data from a node that's not directly connected:

mermaid

graph LR
    A[Node A] --> B[Node B]
    A --> C[Node C]

In Node C's configuration:

jinja

{{ steps.node_A.output_payload.field_name }}
{{ payload.field_name }}

Using in Different Node Types

HTTP Request URL

Direct connection:

jinja

https://api.example.com/users/{{ payload.id }}/profile

Non-direct access:

jinja

https://api.example.com/users/{{ steps.user_lookup.output_payload.id }}/profile

HTTP Request Body

Direct connection:

json

{
  "user_id": "{{ payload.id }}",
  "data": {{ payload | tojson }}
}

Non-direct access (accessing multiple previous nodes):

json

{
  "user_id": "{{ steps.user_lookup.output_payload.id }}",
  "processed_data": {{ steps.process.output_payload | tojson }}
}
JSON in Templates

When embedding JSON from previous steps, use the tojson filter to ensure proper formatting.

Transform Mapping

Direct connection:

json

{
  "email": "{{ payload.email }}",
  "name": "{{ payload.first_name }} {{ payload.last_name }}"
}

Non-direct access:

json

{
  "email": "{{ steps.user_lookup.output_payload.email }}",
  "name": "{{ steps.user_lookup.output_payload.first_name }} {{ steps.user_lookup.output_payload.last_name }}"
}

Filter Condition

Direct connection:

jinja

payload.verified == true

Non-direct access:

jinja

steps.auth_check.output_payload.verified == true

Multiple Outputs

If a node produces multiple output events, each flows independently to downstream nodes.

Use output to access all outputs:

jinja

{{ steps.split_items.output }}
Multiple Outputs

When a node produces multiple outputs, each output becomes a separate event that flows to downstream nodes. This can cause parallel execution paths.

Tips

Best Practices
1

Use descriptive node IDs

Makes references clearer (user_lookup vs node1)

2

Check execution order

Nodes execute in dependency order

3

Test incrementally

Build and test step by step

4

Check logs

See actual output in execution logs

5

Use default filter

Templates render in strict mode, so referencing a missing key raises an error (and can fail the step). Guard optional fields:

jinja

   {{ steps.user_lookup.output_payload.email | default('[email protected]') }}

See Jinja2 Error Handling for per-context behavior.

Example: Complete Example
jinja

{
  "user_id": "{{ steps.user_lookup.output_payload.id }}",
  "email": "{{ steps.user_lookup.output_payload.email | default('no-email') }}",
  "full_name": "{{ steps.user_lookup.output_payload.first_name }} {{ steps.user_lookup.output_payload.last_name }}",
  "timestamp": "{{ meta.received_at }}"
}