---
title: "Save Data"
url: "https://help.flows.world/en/article/save-data"
language: "en"
last_updated: "2026-09-19T07:38:28.723Z"
---

# Save Data

## Overview

**Save Data** stores data in persistent storage for later retrieval within flows or across different flow executions. Use this stage to persist user sessions, maintain state, store temporary or long-term values, or share data between flows.

![](https://cdn-assets.productfruits.com/f9ac4267-b52d-4528-bb61-d263482c7cf4)

\* Example flow demonstrating how **Save Data** stores a player's campaign participation details after a successful qualifying deposit and makes the saved values immediately available for use in later stages.

**Note:** The variable names, values, criteria, and configuration used in this example are for demonstration purposes only.

**Where to find it:** *Actions > Local Storage* in the stage library.

## Configuration

### Required Fields

| Field | Description | Example |
| --- | --- | --- |
| **Group name** | The organizational namespace for your data (groups related data together) | `userSessions`, `orderCache` |
| **Key (Name)** | The identifier for the stored value | `userId`, `lastOrderDate` |
| **Value** | The data to store (static value, variable, or formula starting with =) | `{{userEmail}}`, `John`, `={{price}}*1.1` |

### Also Required

-   **Record name:** **Required.** A sub-grouping within the group name for hierarchical organization (e.g., individual user records within a users group). The builder now marks this as required, and a save with no Record name is rejected.
-   **Time to live (minutes):** **Required.** How long to keep the data before automatic deletion. Use `@forever` for permanent storage, `@1_hour` (60 min), `@24_hours` (1440 min), or a specific number of minutes. A save with no TTL is rejected.

**Using functions in these fields:** Any value field above accepts `@` functions, for example `@NowSecond` for the current time or `@calc(...)` for a calculation. 

See [Flows Functions](https://help.flows.world/en/article/flows-functions) for the full list.

## Exit Points

| Exit | When |
| --- | --- |
| **Pass** | Data successfully saved to storage |
| **Error** | Only for a failure before the save runs (e.g. a variable that can't resolve). A *database* error does NOT reach this exit |

**Important:** a database/storage error during the save does **not** route to the Error exit. It is handled internally and the stage still takes **Pass**, with the error logged. To confirm a save actually landed, follow with a Fetch Data stage and check the value.

## How It Works

When executed, the stage:

1.  **Resolves configuration** - Evaluates group name, record name, TTL, and all key-value pairs
2.  **Saves each key-value pair** - Stores data to persistent storage (one key at a time)
3.  **Updates flow variables** - Adds saved values to flow dictionary for immediate use
4.  **Clears cache (if specified)** - Optionally clears distributed cache across servers

## Time to Live (TTL) Options

| Value | Duration | Use Case |
| --- | --- | --- |
| `@forever` or `0` | Permanent (no expiration) | Configuration settings, permanent counters |
| `@1_hour` | 60 minutes | Short-term caching, rate limiting |
| `@24_hours` | 1440 minutes (24 hours) | Daily data, user sessions |
| Numeric value (e.g., `30`) | Specified minutes | Custom expiration timing |

## Common Use Cases

### 1\. Store User Session

Save user login information for session management.

-   **Group name:** `userSessions`
-   **Record name:** `{{userId}}`
-   **Time to live:** `@24_hours`
-   **Keys:**
    -   `loginTime` → `{{currentTimestamp}}`
    -   `ipAddress` → `{{requestIp}}`
    -   `userEmail` → `{{email}}`

### 2\. Cache API Response

Store API response to avoid repeated external calls.

-   **Group name:** `apiCache`
-   **Record name:** `productData`
-   **Time to live:** `60` (1 hour)
-   **Keys:**
    -   `products` → `{{apiResponse.data}}`
    -   `fetchedAt` → `{{currentTime}}`

### 3\. Store Form Submission

Save multi-step form data as user progresses.

-   **Group name:** `formSubmissions`
-   **Record name:** `{{sessionId}}`
-   **Time to live:** `@forever`
-   **Keys:**
    -   `step1_personalInfo` → `{{formData1}}`
    -   `step2_address` → `{{formData2}}`
    -   `status` → `in_progress`

### 4\. Track Rate Limiting

Store request counts for rate limit enforcement.

-   **Group name:** `rateLimits`
-   **Record name:** `{{userId}}`
-   **Time to live:** `60` (reset hourly)
-   **Keys:**
    -   `requestCount` → `1`
    -   `firstRequest` → `{{timestamp}}`

## Key Behaviors

| Feature | Behavior |
| --- | --- |
| **Overwrite** | Saving to existing group+record+key overwrites the previous value completely |
| **Multiple Keys** | ✓ Save multiple key-value pairs in one stage |
| **Dictionary Update** | ✓ Saved values immediately available as flow variables |
| **TTL Reset** | ✓ Each save resets the expiration timer to the new TTL |
| **Non-Atomic** | Keys saved one at a time (no transaction across multiple keys) |
| **Cache Clear** | ✓ Prefix group name with `cache::` to clear distributed cache |

## Accessing Saved Data

After the stage executes, each saved key is automatically added to your flow variables:

**Example:**

-   Save key `userEmail` with value `john@example.com`
-   Use it immediately as `{{userEmail}}` in subsequent stages
-   To retrieve later (in another flow execution), use Fetch Data stage

## Save Data vs Increment Number

| Use Case | Save Data | Increment Number |
| --- | --- | --- |
| Concurrent updates to counters | ✗ Race conditions - lost updates | ✓ Atomic - safe for concurrent access |
| Storing text/objects/arrays | ✓ Any data type | ✗ Numbers only |
| Single flow updating value | ✓ Simple and sufficient | ✓ Works but overkill |
| Running counters | ✗ Use Increment Number instead | ✓ Designed for this |

**Key rule:** For numeric counters updated by multiple concurrent flows, use Increment Number. For all other data, use Save Data.

## Best Practices

-   ✓ Use descriptive group and key names like `userSessions.loginTime`
-   ✓ Set appropriate TTLs - use `@forever` for permanent data, time-based for temporary
-   ✓ Use Increment Number instead of Save Data for concurrent counter updates
-   ✓ Save multiple related keys together in one stage (more efficient)
-   ✓ Use record names for entity-based organization (e.g., per-user data)
-   ✓ Connect Error exit to handle database connection failures
-   ✓ Document your data schema (which groups, records, and keys are used where)
-   ✓ Use `cache::` prefix when saving frequently accessed data that might be cached

## Common Mistakes

| Mistake | Symptom | Fix |
| --- | --- | --- |
| Using Save Data for concurrent counters | Counter values inconsistent, some updates lost | Use Increment Number for counters updated by multiple flows simultaneously |
| Forgetting TTL for temporary data | Data persists indefinitely, bloats storage | Set appropriate TTL for sessions, caches, temporary tracking data |
| Using inconsistent names | Can't retrieve data - fetch returns defaults | Use exact same group/record/key names in Fetch Data as in Save Data |
| Not handling Error exit | Flow fails silently when database unavailable | Connect Error exit to retry logic or alerting |
| Expecting transactional saves | Partial data saved when error occurs mid-way | Keys saved one at a time; implement custom transaction logic if needed |
| Overwriting data unintentionally | Previous value lost | Remember that saving to existing key overwrites completely (no merging) |

## Troubleshooting

| Issue | Exit/Result | Common Cause | Fix |
| --- | --- | --- | --- |
| Data not persisting | Pass (but data doesn't save) | Very short TTL or data overwritten by another flow | Check TTL value; verify no other flows using same group+record+key |
| Save failed but flow continued anyway | Pass (error swallowed) | A database/storage error is handled internally, so the stage passes instead of taking the Error exit | Check logs for the storage error; use Fetch Data after Save to confirm the value landed |
| Can't retrieve saved data | N/A (affects Fetch Data) | Name mismatch or data expired | Verify exact group/record/key names match; check TTL hasn't expired |
| Counters inconsistent | Pass (but wrong values) | Using Save Data for concurrent counter updates | Switch to Increment Number for atomic counter operations |
| Partial data saved | Error (after some keys saved) | Error occurred mid-way through saving multiple keys | Keys saved before error remain in storage; check which keys succeeded |

## Cache Clearing

Prefix the group name with `cache::` to clear distributed cache when saving:

**Example:** `cache::userSessions`

This will:

-   Save the data to storage
-   Send message to all servers to clear their cached copies
-   Remove value from local memory cache
-   Ensure fresh data is loaded on next read

## Related Stages

-   [**Fetch Data**](https://help.flows.world/en/article/fetch-data)**:** Retrieve data that was saved by this stage
-   [**Delete Data**](https://help.flows.world/en/article/delete-data)**:** Remove saved data when it is no longer needed
-   [**Increment Number**](https://help.flows.world/en/article/increment-number)**:** Safely update numeric counters; use this instead of Save Data for concurrent counters
-   [**Query Data**](https://help.flows.world/en/article/query-data)**:** Search saved data based on configured conditions
-   [**Change Data**](https://help.flows.world/en/article/change-data)**:** Set variables before saving them
