# Set state

Write named variables into the workflow's run context so any later node can read them — no matter how many other nodes ran in between.

## Overview

`last_output` always reflects the previous node only. As soon as another node runs, the old value is gone. **Set state** lets you save a value under a stable name (`customer_email`, `reply_draft`, `page_cursor`…) and reference it 1 or 10 nodes later via `{state.<key>}`.

State persists for the lifetime of a **single run** (including across all While iterations). It is **not** persisted across runs — each new execution starts with a fresh context.

Use it when:

- You'll need the same value **more than once** downstream.
- The value comes from a node that runs **before** an Agent / HTTP call that would otherwise overwrite `last_output`.
- You want a single place to update a workflow-wide constant (signature, default model, channel id…).

## Configuration

| Field | Description |
| --- | --- |
| `key` | Variable name. Use snake_case. Avoid reserved keys (`input`, `last_output`, `event`, `webhook`, `condition_result`, `status`, `result`). |
| `value` | Value to write. Strings support `{placeholders}` — they are resolved against the current context **at write time**. |
| `values` | Object form for batch writes: `{page: 1, cursor: ""}` writes both at once. |

## Reading the state

Two equivalent syntaxes:

| Template | Resolves to |
| --- | --- |
| `{state.greeting}` | The value stored under `greeting` (recommended — explicit namespace) |
| `{greeting}` | Same value (shortcut form) |

## Templating inside `value`

The `value` field accepts the same placeholders as any other node. Examples:

| `value` template | Behavior |
| --- | --- |
| `bonjour` | Stores the literal string `"bonjour"` |
| `{event.body.email}` | Reads the webhook payload, stores the email |
| `{last_output.priority}` | Reads a field from the previous JSON output |
| `{state.first} {state.last}` | Composes a value from earlier state |

> Templates resolve **once**, when the node runs. If `{event.body.email}` is empty at that moment, the literal string `{event.body.email}` is stored — check upstream that the value exists.

## Example: customer reply workflow

```
Webhook → Set state (customer_email = {event.body.from})
        → Set state (language       = {event.body.lang})
        → Agent (draft reply)                ← agent's last_output changes here
        → Set state (reply_draft = {last_output})
        → HTTP Request (POST /send)
            body: {"to": "{state.customer_email}", "subject": "Re: support [{state.language}]", "html": "{state.reply_draft}"}
        → Respond to Webhook
            body: {"status": "sent", "to": "{state.customer_email}"}
```

Without set state, you would lose `customer_email` as soon as the agent runs, and you'd have to read `{event.body.from}` in three different places.

## Gotchas

- The node does **not** modify `last_output` — downstream conditions still see the previous tail node's output.
- Placeholders inside `value` render at write time. A set state at the top of the run can't reference a value that doesn't exist yet.
- `value` arithmetic doesn't work: `{state.x}+1` stores the literal string `"42+1"`. Use a **Transform** node for real math.
- Don't put secrets in state — they're visible in run logs. Use the **Credentials** store.
- An object stored as state can be drilled into: `{state.user.email}`.

## Recent changes

- `value` is now templated at write time (used to be stored literally).
- New `{state.<key>}` namespace alongside the legacy `{<key>}` shortcut.
- New trigger-agnostic `{event.*}` alias: `{event.body.X}` works for webhook, schedule, and event-listener triggers.

---

*Source: https://agentbuilder.systalink.sn/docs/node-set-state — human documentation.*
*Other language: [/docs-md/fr/node-set-state.md](/docs-md/fr/node-set-state.md).*
*Machine-readable index: [/llms.txt](/llms.txt).*
