> ## Documentation Index
> Fetch the complete documentation index at: https://docs.alterscope.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Reduce alert noise: cooldown, persistence, and dedupe

> Stop a single flapping condition from paging you a dozen times — tune cooldown, persistence, and dedupe windows per rule.

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.

<Note>
  These fields live on the rule itself (see [Create and manage alert rules](/recipes/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.
</Note>

## The three controls

| Field                         | Unit    | What it does                                                                                                                                               |
| ----------------------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `cooldown_minutes`            | minutes | After a firing, the rule cannot fire again for the same opportunity until this many minutes pass — even if the condition keeps matching.                   |
| `require_persistence_minutes` | minutes | The 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_minutes`       | minutes | If 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

```bash curl theme={null}
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

```bash curl theme={null}
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

```bash curl theme={null}
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`:

```json theme={null}
{ "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](#validation) above.
