Workflow Memory
Store and retrieve persistent key-value data across executions. Memory lets your workflows remember state — running totals, last-processed items, configuration flags, and more.
What Is Workflow Memory?
By default, each workflow execution starts with a clean slate — no knowledge of previous runs. The Memory node changes that. It provides persistent key-value storage scoped to a workflow, allowing data to survive across executions.
Think of it as a lightweight database for your workflow. Set a value in one execution, read it in the next. This enables stateful automation patterns that would otherwise require an external database.
Memory Operations
| Operation | Description | Output |
|---|---|---|
| Get | Retrieve a value by key. | The stored value, or null if the key does not exist. |
| Set | Store a value. Overwrites if the key already exists. | The stored value (confirmation). |
| Delete | Remove a key and its value. | Boolean success indicator. |
Get
Reads a value from memory. Returns null if the key does not exist, so your workflow can handle the "first run" case gracefully.
// Memory node configuration:
// Operation: Get
// Key: "last_processed_id"
// Output (if key exists):
{ "value": "msg_12345" }
// Output (if key does not exist):
{ "value": null }Set
Writes a value to memory. The value can be any JSON-serializable type — strings, numbers, booleans, arrays, or objects.
// Memory node configuration:
// Operation: Set
// Key: "last_processed_id"
// Value: "{{gmail.messages[0].id}}"
// TTL: 7 days (optional)
// Output:
{ "value": "msg_67890" }Delete
Removes a key from memory. Use this to clean up temporary state or reset counters.
Time-to-Live (TTL)
When setting a value, you can optionally specify a TTL — the duration after which the value is automatically deleted. This prevents memory from growing unbounded over time.
| TTL | Use Case |
|---|---|
| 1 hour | Temporary locks, rate-limit tracking. |
| 24 hours | Daily aggregation counters. |
| 7 days | Weekly report state. |
| 30 days | Monthly processing checkpoints. |
| No TTL | Permanent configuration (until manually deleted). |
Cleanup
Memory Scope
Memory is scoped to the individual workflow. Two workflows cannot read each other's memory. This provides isolation — a bug in one workflow cannot corrupt another's state.
Within a workflow, all executions share the same memory space. This is by design — it allows one execution to write state that the next execution reads.
Common Patterns
Deduplication
Store the ID of the last processed item. On each run, read the stored ID and skip items that have already been processed.
// Start of workflow:
// Memory Get → key: "last_email_id" → returns "msg_100"
// Gmail List Messages → returns messages [msg_103, msg_102, msg_101, msg_100]
// Condition: loop.item.id > memory.value
// → Process only msg_103, msg_102, msg_101
// End of workflow:
// Memory Set → key: "last_email_id" → value: "msg_103"Running Counters
Track counts across executions — how many emails processed, how many errors encountered, how many approvals granted.
// Memory Get → key: "emails_processed_today"
// If null, set to 0
// Increment by number of emails processed in this run
// Memory Set → key: "emails_processed_today" → value: 42
// (with 24-hour TTL to auto-reset daily)Runtime Configuration
Store workflow configuration values in memory so they can be changed without editing the workflow. An admin can set a threshold, channel name, or filter criteria via a separate "config updater" workflow.
State Machine
Track the current state of a multi-step process (e.g., order fulfillment). Each execution reads the state, performs the appropriate action, and updates the state for the next run.
Limits & Best Practices
- Keep values small — memory is not designed for large datasets. Store IDs, counters, and configuration, not full API responses.
- Use descriptive key names — 'last_processed_id' is better than 'id'. Key names are per-workflow, but clarity helps maintenance.
- Set TTL for temporary data — avoid unbounded memory growth.
- Handle null gracefully — always check if a Get returns null (first run scenario).
- Values must be JSON-serializable — strings, numbers, booleans, arrays, and plain objects.