Protections — cooldown, backoff, limits
Last updated: May 19, 2026
Protections — cooldown, backoff, limits
Wevion enforces 5 protections to prevent rules from causing harm: cooldown (per-entity time-between-acts), max executions per day, budget change limits, pending-action check, and a circuit breaker that auto-pauses misbehaving rules. Plus exponential backoff on failures.
Who is this for
Anyone designing rules that change live spend. Protections are how you sleep at night.
Why protections matter
A rule without protections can:
Pause the same adset repeatedly as transient metric blips trigger conditions
Increase budget 10× in one day chasing a noisy ROAS spike
Cascade errors when the platform API is down, locking the rule in failure mode
Each protection guards a specific failure mode.
Protection 1: cooldown_minutes
After acting on an entity, the rule won't act on that entity again for cooldown_minutes.
Default: 360 (6 hours)
Minimum for destructive actions (pause, decrease_budget_pct, relaunch): 60 minutes
Per-entity scope: cooldown is per-rule-per-entity; the same rule can still act on OTHER entities during cooldown
Use case: prevents thrashing when a metric oscillates around the threshold. Adset's CPA crosses 30 → pause. 5 minutes later metric refreshes and shows 29.50 → without cooldown, would activate. With cooldown, action holds for 6 hours.
Protection 2: max_executions_per_day
Hard cap on total executions across all entities, per rule, per UTC day.
Default: 3
Hard cap: 50 (cannot raise above this)
Use case: a scale-winner rule capped at 3/day prevents runaway scaling. If 5 adsets all hit the ROAS threshold today, only the first 3 are acted; others are skipped with daily_cap_reached reason.
Protection 3: budget_change_limit_pct
Caps the % delta of any single budget action.
Example: budget_change_limit_pct: 50 + rule action increase_budget_pct: 100 → action capped at +50%. Recorded with note that limit was applied.
Use case: hard safety on increase_budget_pct rules. Even if rule says +100%, never apply more than +50% in one shot.
Protection 4: budget_daily_cap
Cumulative cap on total budget delta per entity per UTC day.
Example: budget_daily_cap: 100 (USD) + adset receives 4 separate +20% increases summing to $120 → 4th action capped to keep total at $100.
Use case: layered safety — even if max_executions_per_day allows 3 increases, total $ delta is bounded.
Protection 5: pending check (30 min)
Before executing, the worker checks: is there already a pending action for this rule + entity in the last 30 minutes? If yes, skip + record reason.
Prevents duplicate actions when:
Two consecutive cron ticks both enqueue the rule (rare edge case)
The previous action is still in flight (SQS retry, slow platform API)
Not user-configurable; always active.
Circuit breaker (auto-pause)
If a rule's error rate goes critically high, it auto-pauses to status: error.
Trigger conditions (both must hold):
Error rate ≥ 50% over recent executions
At least 20 total errors in the settle window
When tripped:
Rule status flips to
errorauto_paused_attimestamp setauto_pause_reasonfield captures causecb_reset_aftertimestamp marks when auto-resume eligibility startsNotification sent (if notify_on_execution + notify_channels set)
To recover: review last_error, fix the upstream issue (e.g. token expired, platform API change), then manually toggle status back to active. Or wait until cb_reset_after for automatic resume eligibility.
Backoff (per-execution retries)
For each execution attempt that fails (API timeout, transient error):
Max 5 attempts over 24 hours
Exponential backoff between attempts
After 5 failures: marked
entities_erroredwith last error message
This is per-entity-per-execution, distinct from circuit breaker (which is per-rule across executions).
Protection priority order
When multiple protections could apply, they're checked in this order:
Rule status (paused / error → skip)
Schedule (not due yet → skip)
Daily cap (
max_executions_per_dayreached → skip)Pending check (in-flight action → skip)
Cooldown (within window → skip)
Budget caps (apply on execute)
Action attempted; on failure → backoff
Each skip is recorded in entities_skipped with the specific reason.
Visible in execution history
/rules/:id → execution timeline shows:
entities_skippedwith breakdown per reason:cooldown_active,daily_cap_reached,pending_action_exists,budget_cap_reached,entity_state_conflictentities_erroredwith last error message + attempt count
Useful for tuning: if everything is skipped for daily_cap_reached, raise the cap or accept the throttle.
Recommended defaults
For new rules:
Cooldown: 360 min (6h) — default. Drop to 60 only for explicit reasons.
Max executions/day: 3 (default). Raise carefully.
Budget change limit: 30% (single-action). 50%+ only for proven rules.
Budget daily cap: 50-100% of entity's average daily budget. Limits cumulative drift.
Common mistakes
Cooldown too short on pause action: rule re-pauses + re-activates on metric noise. Default 360 min exists for this reason.
No daily cap on increase_budget_pct: rule scales 3× in one day chasing noisy ROAS spike. Set
budget_daily_capalways.Resuming error-status rule without fixing root cause: immediately re-trips circuit breaker. Review
last_errorfirst.Setting
max_executions_per_day: 50: hard cap; rule can fire 50 times in a day. Almost always overkill — start at 3.
Related
Actions overview — what actions are gated by these protections
Best practices — recipes that use protections well
Troubleshooting — debugging when protections seem wrong