Limit Route
Overview
Limit route lets a flow pass through this point only once per time window you choose. After a run takes the Pass path, the route is locked for the duration you set; any run that arrives before the window ends takes the Fail path instead. Use it for rate limiting, cooldowns, deduplication, and throttling expensive actions.
Where to find it: Flow Control in the stage library.
When to Use
- Prevent duplicate actions within a time window
- Rate limit per user, per request, or per any other entity
- Throttle expensive operations, such as API calls or emails
- Enforce a cooldown period between actions
- Protect against rapid, repeated executions or accidental spam
- Debounce a flow that's triggered repeatedly in quick succession
Configuration
Required Fields
| Field | Description | Example |
|---|---|---|
| Time in Seconds | How long the route stays locked after a run takes the Pass path | 3600 (1 hour), 300 (5 minutes) |
Optional Fields
- Limit Name (Advanced): A custom identifier for this lock. If left blank, it defaults to a name based on the stage and flow. Use the same Limit Name across different flows to share one limit between them. Can include
{{variables}}for per-user or per-entity limits, e.g.email_sent_{{userId}}. - Comment: Documentation only, no effect on how the stage runs.
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 | No lock is currently active (first run, or the previous window has expired). A new lock is created. |
| Fail | A lock is already active. The run takes Fail immediately - it does not wait or queue for the window to end and then pass. Route to alternative handling or a message for the user. If you want a delayed retry, send the Fail path to Run Later or Wait a while yourself. |
How It Works
When executed, the stage:
- Resolves the Limit Name - Uses your custom name if provided, otherwise a name based on the stage and flow, always scoped to your workspace
- Checks and creates the lock in one step - So two runs that arrive at almost the same instant can't both slip through; only one of them takes the Pass path
- Routes based on the result - No lock existed: takes Pass and creates a new one. A lock already existed: takes Fail
- Lets the lock expire automatically - After the Time in Seconds you set, the next run after that takes Pass again
Rate limiting model: Sliding window. The window restarts every time a run takes the Pass path, it isn't tied to a fixed clock time like the top of the hour.
Examples
1. Prevent Duplicate Email Sends
- Limit Name:
email_sent_{{userId}} - Time in Seconds:
3600(1 hour) - Pass path: Send Email → End
- Fail path: End (skip the email)
- Result: At most one email per hour per user
2. API Rate Limiting
- Limit Name:
api_call_limit - Time in Seconds:
6(enforces roughly 10 calls per minute) - Pass path: Call API → continue
- Fail path: return an error message
- Result: At least 6 seconds between calls
3. User Action Cooldown
- Limit Name:
reward_claimed_{{userId}} - Time in Seconds:
86400(24 hours) - Pass path: save the reward
- Fail path: return "Already claimed today"
- Result: One reward claim per day per user
4. Shared Limit Across Flows
- Limit Name:
notification_limit_{{userId}}(used the same way in every flow) - Time in Seconds:
3600 - Flows: order confirmations, marketing, system alerts
- Result: All three flows share one limit and coordinate automatically
5. Debouncing Rapid Triggers
- Limit Name:
file_process_{{fileId}} - Time in Seconds:
30 - Pass path: process the file
- Fail path: ignore the rapid change
- Result: The file is processed at most once every 30 seconds, no matter how often it changes
Key Behaviors
| Feature | Behavior |
|---|---|
| Distributed | ✓ Works across every server behind your workspace; the limit is shared, not per-server |
| Workspace Isolation | ✓ Limits are scoped to your workspace; other workspaces can't affect or see yours |
| No Race Condition | ✓ Checking for a lock and creating one happen as a single step, so two simultaneous runs can't both take Pass |
| Blocked Run | Routes to Fail immediately; it does not wait or queue for the window to end |
| Time Precision | 1-second precision; sub-second windows aren't supported |
| Window Type | Sliding: resets from the last Pass, not a fixed clock time |
| Fails Open | If a technical issue prevents the check, the flow takes the Pass path rather than getting stuck |
| No Output Variables | The stage doesn't add any data to the flow; it only routes Pass or Fail |
Best Practices
- ✓ Use descriptive Limit Names that make it clear what's being limited
- ✓ Include an entity ID in the name for per-user or per-item limits, e.g.
{{userId}} - ✓ Choose a time window that balances protection with a good user experience
- ✓ Always handle the Fail path with a clear message or fallback action
- ✓ Reuse the same Limit Name across flows only when you want them to share one limit
- ✓ Combine with other controls, such as authentication, for anything security-critical
Common Mistakes
| Mistake | Symptom | Fix |
|---|---|---|
| Missing variables in the Limit Name | Using limit_{{userId}} but userId isn't set yet | Make sure the variable has a value before this stage runs |
| Unintended name collision | Two different actions share a limit by accident | Use a specific name that includes context, e.g. action_entity_id |
| Not handling the Fail path | The flow appears to break when the limit is hit | Always connect the Fail exit to appropriate handling |
| Expecting exact timing | Assuming millisecond precision | Work at the second level and add buffer for anything time-sensitive |
| Relying on it for security | Treating the limit as a strict guarantee | Combine with database-level constraints for anything critical; this stage can fail open |
| Expecting fixed windows | Assuming a "top of the hour" style reset | Remember the window is sliding and resets from the last Pass |
Troubleshooting
| Issue | Common Cause | Fix |
|---|---|---|
| Limit doesn't seem to apply; always Pass | A temporary technical issue caused the stage to fail open | If this keeps happening, contact your Flows support contact |
| Unexpected limit collisions | The same Limit Name is used for two different things | Use a more specific name that includes an entity ID |
| Variable not resolving in the Limit Name | The variable was empty or not set yet | Confirm the variable has a value before this stage runs |
| Limit resets sooner or later than expected | The sliding window resets from the last Pass, not a fixed clock time | Plan around the last successful run rather than calendar time |
Edge Cases
- Very short windows: Minimum is 1 second; sub-second windows aren't supported
- Default Limit Name: Different stages in the same flow have independent limits; copying a stage to a new flow creates a new limit
- Empty variable in the name: Results in a name with a gap, e.g.
limit__action; make sure variables resolve as expected - Manual reset: A lock can be cleared early with the Limit route Reset stage; use carefully so it doesn't defeat the purpose of the limit
Related Stages
- Reset Limit: Manually clear a lock before it expires
- Route Flow: Branch based on variable values instead of execution rate
- Loop: Repeat part of a flow, with delays between iterations
- Run Later: Schedule delayed execution instead of blocking it