Schedule rules are IF→THEN policies that Timely evaluates whenever you save a schedule. They let you encode "we never schedule more than 40 hours a week" or "every closing shift needs at least one Manager-on-Duty" once, and have the system enforce them automatically.
Pro plan and up.
Where rules live
/dashboard/rules (sidebar nav). Each row is a rule with:
- A condition (the IF — what trigger to look for)
- An action (the THEN — WARN or BLOCK)
- A scope (which locations, employment types, or tags it applies to)
- An enabled toggle
Disabled rules stay configured but don't fire — useful for seasonal policies or experiments.
The conditions
| Condition | What it triggers on |
|---|---|
| Weekly hours exceed N | An employee's total scheduled hours for the week go over N |
| Daily hours exceed N | An employee's total hours on a single day go over N |
| Shift longer than N hours | Any single shift's duration exceeds N |
| Consecutive work days exceed N | An employee works more than N days in a row |
| Less than N hours rest between shifts | The gap between two consecutive shifts is less than N |
| Tag missing on shift | A scope-matching shift doesn't have a required tag |
| Tag count below minimum | A day/shift slot has fewer than N employees with the required tag |
You can chain conditions with AND/OR by creating multiple rules — each rule is evaluated independently.
WARN vs. BLOCK
- WARN — the violation shows a yellow banner on the schedule grid. The save still goes through. Use this for soft preferences.
- BLOCK — the violation prevents saving. The Save button is disabled until the conflict is resolved. Use this for hard requirements (legal, safety, operational).
Most rules start as WARN — you want to see how often they trigger before flipping to BLOCK.
Scoping
By default a rule applies to every employee at every location. To narrow:
- Locations — only fires for shifts at the selected locations
- Employment types — only applies to full_time / part_time / contractor as selected
- Tags — only applies to employees with at least one of the selected tags
Scoping is "include only" — there's no exclude option. To exclude, scope by everything except the thing you want excluded.
Examples
Hard cap on overtime
- Condition: Weekly hours exceed 40
- Action: BLOCK
- Scope: All hourly employees (employment_type: hourly)
This prevents anyone hourly from being scheduled into overtime. Salaried employees are exempt because of the scope.
Coverage requirement
- Condition: Tag count below minimum (Closer, ≥ 2)
- Action: WARN
- Scope: All locations, evening shifts (you'd narrow this in the rule's day/time picker)
Warns if any closing shift doesn't have at least 2 closers scheduled.
Rest period
- Condition: Less than 10 hours rest between shifts
- Action: BLOCK
- Scope: All employees
A common labor-law-driven rule — prevents back-to-back closing-into-opening shifts.
When rules evaluate
Server-side, on every save. The flow:
- Manager edits a shift → clicks Save
- The schedule API runs the rules engine over the entire week's grid
- Any WARN violations come back in the response and surface as banners
- Any BLOCK violations cause the save to fail with a list of unresolved issues
The evaluation is fast (typically <100ms per save), so it doesn't slow editing.
Editing rules
Edits take effect on the next save — they don't retroactively flag existing schedules. If you want to retroactively check a past week against a new rule, open that week's grid and trigger any save (e.g., toggle and re-toggle the SMS alert flag); the engine will re-evaluate against the current rule set.
Audit log
Every triggered violation is logged for the org admin's audit history (visible to platform staff at /admin/communications for support purposes). This helps with "did Sarah get scheduled into overtime last quarter?" questions.
Limits
| Plan | Active rules |
|---|---|
| Pro | 10 |
| Business | 50 |
| Enterprise | Unlimited |
Disabled rules don't count.