How State Moves Between Blocks
Every block can read from earlier blocks using double-brace template references:{{ inputs.field_name }}— reads a field from the Input block (the workflow’s trigger payload){{ block_id.output }}— reads the full output of a prior block by its ID{{ block_id.some_field }}— reads a specific field from a prior block’s output
A Practical Example
Consider a three-block chain: an Input block, anenrich_user block that looks up account details, and a send_email block that composes a message.
Conditional Logic
Use Jinjaif/elif/else blocks to branch on values in your state. This is useful for routing and for shaping dynamic content.
Route by account tier:
Global Variables
Scout exposes built-in date and time variables you can use in any template without configuration. They’re handy for timestamps, logging, and report headers.
For example:
State Design Tips
A few habits keep workflow state clean as it grows:- Keep payloads small. Pass only the fields a block actually needs, not the whole state object.
- Use stable key names. Renaming an output field silently breaks every downstream reference to it.
- Normalize once. Clean and format data in one early block, then reuse the normalized values everywhere downstream.
- Prefer structured output over string building. Return structured data like
{ "user_id": "abc", "status": "active" }rather than a formatted string — it’s far easier to branch on later.
Error and Fallback Paths
Don’t assume the happy path. Add branch logic for the failures you can anticipate:- Missing or empty input fields
- Empty search or lookup results
- External API failures or timeouts
ok field:
Next Steps
Blocks
Choose the right block type for each step in your workflow.
Creating Workflows
Assemble blocks with the validate-fetch-decide-act-return pattern.
Running Workflows
Execute your workflow and inspect block-level state in the Console.
Logs
Trace block-by-block execution and debug failures in production.