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.
* 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
@foreverfor 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 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:
- Resolves configuration - Evaluates group name, record name, TTL, and all key-value pairs
- Saves each key-value pair - Stores data to persistent storage (one key at a time)
- Updates flow variables - Adds saved values to flow dictionary for immediate use
- 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→1firstRequest→{{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
userEmailwith value[email protected] - 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
@foreverfor 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: Retrieve data that was saved by this stage
- Delete Data: Remove saved data when it is no longer needed
- Increment Number: Safely update numeric counters; use this instead of Save Data for concurrent counters
- Query Data: Search saved data based on configured conditions
- Change Data: Set variables before saving them