> ## Documentation Index
> Fetch the complete documentation index at: https://docs.scoutos.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Logic and State: How Data Flows Between Blocks

> Understand how Scout workflows pass data between blocks through shared state — references, conditional logic, global variables, and error handling.

A Scout workflow moves data through a shared state object that's passed from block to block. Each block reads what it needs from earlier blocks, does its job, and adds its own output back to the state for downstream blocks to use. Designing that state deliberately is what makes a workflow predictable, testable, and easy to debug.

## 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

Reference the block by the ID you gave it, then the field you want. The reference is resolved at runtime with the current state.

### A Practical Example

Consider a three-block chain: an Input block, an `enrich_user` block that looks up account details, and a `send_email` block that composes a message.

```jinja theme={null}
{# In the enrich_user block — read from the workflow inputs #}
User name: {{ inputs.user_name }}
Account tier: {{ inputs.account_tier }}
```

```jinja theme={null}
{# In the send_email block — read from the enrich_user block #}
Full name: {{ enrich_user.full_name }}
Plan label: {{ enrich_user.plan_label }}
```

Each block pulls only the fields it needs. Map references explicitly rather than passing the whole state object around — when something breaks, you want to see exactly which value a block depended on.

<Tip>
  Reference the specific fields a block needs instead of handing it the entire upstream output. Explicit mappings make failures obvious in the logs.
</Tip>

## Conditional Logic

Use Jinja `if`/`elif`/`else` blocks to branch on values in your state. This is useful for routing and for shaping dynamic content.

Route by account tier:

```jinja theme={null}
{% if inputs.account_tier == "enterprise" %}
  Priority support queue — SLA: 4 hours
{% elif inputs.account_tier == "pro" %}
  Standard support queue — SLA: 24 hours
{% else %}
  Community support — check our docs first
{% endif %}
```

Build a dynamic prompt that adapts to the input:

```jinja theme={null}
You are a support assistant.
{% if inputs.has_prior_conversation %}
The user has contacted us before. Be warm and reference their history.
{% else %}
This is a new user. Introduce yourself briefly.
{% endif %}
User message: {{ inputs.message }}
```

## 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.

| Variable                                  | Example output        |
| ----------------------------------------- | --------------------- |
| `{{ __exp_global.current_date }}`         | `2025-06-15`          |
| `{{ __exp_global.current_time }}`         | `14:23:01`            |
| `{{ __exp_global.current_datetime }}`     | `2025-06-15 14:23:01` |
| `{{ __exp_global.current_time_utc }}`     | `21:23:01 UTC`        |
| `{{ __exp_global.current_time_pacific }}` | `14:23:01 PDT`        |

For example:

```jinja theme={null}
[{{ __exp_global.current_datetime }}] User {{ inputs.user_id }} submitted request
Report generated on {{ __exp_global.current_date }}
```

## 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

Return explicit status fields so downstream blocks can branch on them. A lookup block might return either of these shapes:

```json theme={null}
{ "ok": true, "data": { "user_id": "abc", "email": "user@example.com" } }
```

```json theme={null}
{ "ok": false, "error_code": "USER_NOT_FOUND", "message": "No user found for that ID" }
```

Then branch on the `ok` field:

```jinja theme={null}
{% if lookup_user.ok %}
  Found {{ lookup_user.data.email }} — continuing workflow
{% else %}
  Stopping: {{ lookup_user.error_code }} — {{ lookup_user.message }}
{% endif %}
```

This keeps error handling explicit and auditable rather than buried inside block logic.

## Next Steps

<CardGroup cols={2}>
  <Card title="Blocks" icon="cube" href="/workflows/blocks">
    Choose the right block type for each step in your workflow.
  </Card>

  <Card title="Creating Workflows" icon="hammer" href="/workflows/creating-workflows">
    Assemble blocks with the validate-fetch-decide-act-return pattern.
  </Card>

  <Card title="Running Workflows" icon="play" href="/workflows/running-workflows">
    Execute your workflow and inspect block-level state in the Console.
  </Card>

  <Card title="Logs" icon="magnifying-glass" href="/workflows/logs">
    Trace block-by-block execution and debug failures in production.
  </Card>
</CardGroup>
