How to Share Data Between Steps in Probe Using Step Outputs: A Complete Guide
Probe enables data sharing between steps through step outputs, where steps declare an id and an outputs map to expose values that subsequent steps can access via the {{outputs.<id>.<name>}} expression syntax.
Probe is a lightweight workflow automation tool designed for HTTP-based testing and orchestration. When building multi-step workflows, you need to share data between steps in Probe using step outputs—such as authentication tokens, session IDs, or dynamic values extracted from API responses.
Understanding Step Outputs in Probe
Probe's output system consists of three core components working together: the Step definition, the Outputs registry, and the expression engine.
The Core Architecture
The Step struct in step.go defines how outputs are declared:
Step.Outputs(lines 29-30): A map where keys are output names and values are expressions that extract data from the step's responseStep.saveOutputs(lines 41-67): Evaluates each output expression using the step's context and writes results to the globalJobContext.OutputsregistryOutputsstruct (lines 9-34 inoutputs.go): Central store providingSet,Get,GetAll, andGetAllWithFlatmethods for managing step-based and flat outputs
How Data Flows Between Steps
When a step executes, Probe builds a data pipeline:
- Step A finishes execution and calls
saveOutputs(), which evaluates expressions likeres.body.access_tokenand stores results viaOutputs.Set() - Step B begins execution via
Step.SetCtx()(lines 20-34), which loads all previously saved outputs into the step's evaluation context usingj.Outputs.GetAll() - The expression engine (
expr.go) resolves templates like{{outputs.login.token}}against this combined context
Defining Step Outputs in YAML
To share data between steps in Probe using step outputs, you must explicitly declare which values each step produces.
Basic Output Declaration
Every producing step requires an id field and an outputs map:
- name: Authenticate with API
id: login # Required identifier for referencing
uses: http
with:
post: /auth/login
body:
username: admin
password: secret
outputs:
token: res.body.access_token
expiry: res.body.expires_in
The id field creates the namespace (login) under which these outputs are stored. Without an id, subsequent steps cannot reference the data.
Extracting Values from HTTP Responses
Probe evaluates output expressions against the step's response context. Common extraction patterns include:
res.body.<field>for JSON response bodiesres.headers.<name>for header valuesres.codefor HTTP status codes
- name: Create Resource
id: creator
uses: http
with:
post: /api/resources
body:
name: test-resource
outputs:
resource_id: res.body.id
created_at: res.body.timestamp
Consuming Step Outputs in Subsequent Steps
Once a step defines outputs, any subsequent step in the same job—or in dependent jobs—can access those values using Probe's expression syntax.
Referencing Outputs with Expression Syntax
Use the {{outputs.<step_id>.<output_name>}} template syntax to retrieve values:
- name: Fetch Protected Data
uses: http
with:
get: /api/protected/data
headers:
Authorization: "Bearer {{outputs.login.token}}"
test: res.code == 200
Probe's expression engine resolves this template at runtime by looking up login in the outputs registry and retrieving the token field.
Flat Access Shortcuts
When no naming conflicts exist, Probe creates flat shortcuts that allow direct access without the step ID prefix:
- name: Quick Token Access
uses: http
with:
get: /api/verify
headers:
Authorization: "Bearer {{outputs.token}}"
According to the source code in outputs.go (lines 43-52), the Set method automatically creates these flat entries when the output name doesn't collide with existing keys. If two steps both output a field named token, the flat shortcut is omitted to prevent ambiguity, and you must use the scoped syntax {{outputs.step1.token}} or {{outputs.step2.token}}.
Cross-Job Data Sharing with Needs
Probe extends step outputs across job boundaries using the needs dependency system. When a job declares needs, Probe ensures the dependent job waits for completion and receives access to all outputs from the required jobs.
jobs:
- name: Authentication Job
id: auth
steps:
- name: Login
id: login
uses: http
with:
post: /auth
outputs:
token: res.body.access_token
- name: Data Processing
needs: [auth] # Waits for auth job to complete
steps:
- name: Fetch Data
uses: http
with:
get: /api/data
headers:
Authorization: "Bearer {{outputs.login.token}}"
The workflow.go file orchestrates this execution order, ensuring that the auth job's saveOutputs calls complete before the Data Processing job begins building its step contexts via SetCtx.
Conflict Handling and Best Practices
Output Name Collisions
The Outputs.Set method in outputs.go (lines 27-36) implements conflict detection. If a step's id collides with an existing flat output name, the method returns an error and retains only the step-scoped map, preventing accidental data overwrites.
To avoid collisions:
- Use descriptive, unique step IDs like
auth_loginrather than generic names likestep1 - Namespace your outputs when multiple steps produce similar data (e.g.,
user_idvsadmin_id)
Best Practices for Naming
When you share data between steps in Probe using step outputs, follow these conventions:
- Use snake_case for step IDs: The
idfield becomes a namespace in expressions, sofetch_user_datais more readable thanfetchUserDatain YAML templates. - Document output schemas: Since Probe evaluates expressions dynamically, document what fields each step outputs (e.g.,
token,expires_at) in workflow comments. - Validate outputs with tests: Use the
testfield to ensure outputs exist before downstream steps consume them:
outputs:
token: res.body.token
test: outputs.token != "" # Fails step if extraction fails
Summary
- Declare outputs by adding an
idto your step and defining anoutputsmap with extraction expressions. - Access values in subsequent steps using the
{{outputs.<step_id>.<output_name>}}expression syntax. - Use flat shortcuts like
{{outputs.token}}when no naming conflicts exist, but prefer scoped access for clarity. - Share across jobs by declaring
needsdependencies, which ensure output availability before dependent jobs execute. - Avoid collisions by using descriptive step IDs, as the
Outputs.Setmethod inoutputs.goprevents overwrites but may omit flat shortcuts when conflicts occur.
Frequently Asked Questions
What is the syntax for accessing step outputs in Probe?
Use the template expression {{outputs.<step_id>.<output_name>}} to access values. For example, if a step with id: login defines an output named token, reference it as {{outputs.login.token}}. If no naming conflicts exist, you can also use the flat syntax {{outputs.token}} for direct access.
Can I share data between different jobs in Probe?
Yes. Declare a needs dependency in the consuming job to ensure the producing job completes first. Once the dependency is satisfied, you can reference outputs from any step in the required job using the same {{outputs.<step_id>.<output_name>}} syntax, as the workflow.go orchestrator ensures outputs are persisted before dependent jobs begin.
What happens if two steps define outputs with the same name?
Probe handles this through conflict detection in outputs.go. If two steps output fields with identical names, the system retains both step-scoped values (accessible via {{outputs.step1.name}} and {{outputs.step2.name}}), but omits the flat shortcut ({{outputs.name}}) to prevent ambiguity. If a step ID collides with a flat output name, Outputs.Set returns an error and preserves only the step-scoped map.
How does Probe handle output extraction from HTTP responses?
Probe evaluates output expressions against the step's response context after execution completes. In the outputs map, write expressions like res.body.access_token for JSON fields, res.headers.location for headers, or res.code for status codes. The Step.saveOutputs method (lines 41-67 in step.go) evaluates these expressions using the step's context and stores the results in the global Outputs registry, making them available to subsequent steps via the expression engine.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →