Skip to main content
A metric that hovers near your threshold can cross it, fall back, and cross it again within minutes. Without noise control, that’s one notification per crossing. Three independent fields on every rule — set at creation or added later with a partial-merge PUT — cut that down before a message is ever sent.
These fields live on the rule itself (see Create and manage alert rules) — there’s no separate noise-control endpoint. All three are optional; omit them and the rule fires on every condition match, every time.

The three controls

FieldUnitWhat it does
cooldown_minutesminutesAfter a firing, the rule cannot fire again for the same opportunity until this many minutes pass — even if the condition keeps matching.
require_persistence_minutesminutesThe condition must hold continuously for this long before it counts as a fire. A one-tick blip that reverts a minute later never fires.
dedupe_window_minutesminutesIf the rule would fire again with the same condition values as its last firing, and less than this many minutes have passed, the repeat is suppressed.
They apply in this order: persistence gates whether a fire is considered at all, then cooldown and dedupe gate whether that fire is actually delivered.

1. Cooldown — enforce a minimum gap between firings

curl
curl -H "Authorization: Bearer $ALTERSCOPE_API_KEY" \
     -H "Content-Type: application/json" \
     -X POST -d '{
       "name": "USDC APY drop",
       "data_source": "yield_radar",
       "asset_symbol": "USDC",
       "conditions": [{ "field": "apy", "operator": "lt", "value": 5.0 }],
       "message_template": "{{.asset_symbol}} APY fell to {{.apy}}%",
       "cooldown_minutes": 60
     }' \
  "https://api.alterscope.org/v1/alerts/rules"
With cooldown_minutes: 60, once this rule fires for a given opportunity, it won’t fire again for that same opportunity for an hour — regardless of how many times the APY dips below 5% and recovers in between. A different opportunity (a different asset/venue pair the rule also watches) has its own independent cooldown clock.

2. Persistence — require the condition to hold, not just touch

curl
curl -H "Authorization: Bearer $ALTERSCOPE_API_KEY" \
     -H "Content-Type: application/json" \
     -X PUT -d '{ "require_persistence_minutes": 15 }' \
  "https://api.alterscope.org/v1/alerts/rules/$RULE_ID"
require_persistence_minutes: 15 means the condition has to stay true for a continuous 15-minute stretch before the rule fires. If the APY drops below 5% and climbs back above it two minutes later, that dip never fires — it didn’t persist long enough. This is the field to reach for when a source is naturally noisy (perp funding rates, thin-liquidity APYs) and single-tick crossings aren’t actionable on their own.

3. Dedupe — collapse repeats of the same values

curl
curl -H "Authorization: Bearer $ALTERSCOPE_API_KEY" \
     -H "Content-Type: application/json" \
     -X PUT -d '{ "dedupe_window_minutes": 30 }' \
  "https://api.alterscope.org/v1/alerts/rules/$RULE_ID"
Cooldown suppresses any re-fire; dedupe only suppresses a re-fire that reports the same condition values as the previous one. Set both together when you want “at most one alert per half hour, and definitely not two identical ones back to back.”

Validation

All three fields reject negative values with 400:
{ "error": "require_persistence_minutes must be non-negative" }
The same applies to dedupe_window_minutes. There’s no upper bound enforced server-side — pick a window that matches how often the underlying data actually refreshes; a require_persistence_minutes longer than your data source’s polling interval will never be satisfiable.

Troubleshooting

  • A rule I expected to fire didn’t: check require_persistence_minutes first — a condition that flickers in and out never accumulates a persistent match. Pull GET /v1/alerts/events?rule_id=$RULE_ID&status=suppressed to see fires the platform held back and why.
  • I’m still getting near-duplicate alerts seconds apart: dedupe_window_minutes only collapses fires with identical condition values; if the underlying value is still moving (APY ticking down by 0.1% each time), each fire looks different and dedupe won’t merge them — use cooldown_minutes instead, which doesn’t care whether the values match.
  • 400 on create or update: re-check for a negative value on require_persistence_minutes or dedupe_window_minutes — see Validation above.