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

# Frequency Caps

> Enforce delivery caps on offers, categories, and channels to control campaign spend and audience saturation.

<Note>
  **See also**: [Frequency Caps REST API reference](/api-reference/frequency-caps) for request/response shapes, status codes, and error semantics.
</Note>

# Frequency Caps

Frequency caps are system-wide delivery caps that automatically block offers when their budget is exhausted. They operate independently of contact policies — contact policies protect individual customers, while frequency caps protect the business from over-delivery.

## Cap Scopes

| Scope        | What It Controls                                              | Example                                  |
| ------------ | ------------------------------------------------------------- | ---------------------------------------- |
| **global**   | Caps total delivery across every offer, category, and channel | "Whole tenant: max 100k impressions/day" |
| **offer**    | Caps delivery of a specific offer                             | "Win-Back: max 20 impressions/day"       |
| **category** | Caps all offers in a category                                 | "Auto Insurance: max 500/day"            |
| **channel**  | Caps all delivery on a channel                                | "Direct Mail: max 50/day"                |

<Note>
  A `global`-scoped cap needs no `scopeId`. When its budget is exhausted it excludes **every** candidate at decision time, and each response increments it once — symmetric with `offer`, `category`, and `channel` caps.
</Note>

## How It Works

1. Frequency caps are checked during the **Contact Policy** stage of the decision pipeline
2. The system queries `frequency_caps` for the tenant where `enabled=true` and `resetAt > now()`
3. Any cap where `currentCount >= maxVolume` adds its scope to the exclusion set
4. Candidates matching excluded scopes (offer, category, or channel) are filtered out

## Counter Increment

The `currentCount` is incremented by the **Respond API** when an impression outcome is recorded:

* Only impression-type outcomes increment the counter (clicks/conversions don't)
* The increment is atomic (prevents race conditions in concurrent delivery)
* Counters reset when `resetAt` passes (configured per cap)

## Configuration

Frequency caps are stored in the `frequency_caps` table:

| Field          | Type      | Description                                |
| -------------- | --------- | ------------------------------------------ |
| `name`         | string    | Human-readable name                        |
| `scope`        | enum      | `offer`, `category`, or `channel`          |
| `scopeId`      | string    | ID of the offer, category, or channel      |
| `maxVolume`    | integer   | Maximum delivery count per period          |
| `period`       | string    | Reset period: `daily`, `weekly`, `monthly` |
| `currentCount` | integer   | Current delivery count (auto-incremented)  |
| `resetAt`      | timestamp | When the counter resets                    |
| `enabled`      | boolean   | Whether the cap is active                  |

## Best Practices

* Set channel-level caps for batch channels (Direct Mail, SMS) to control vendor costs
* Set offer-level caps for limited-inventory promotions
* Monitor `currentCount` vs `maxVolume` in the Operations dashboard
* To keep a channel's placements consistent when a cap empties one of them, use the channel's coupling mode (`Channel.couplingMode`, or a per-flow `DecisionFlow.couplingOverride`). The old Group-node `allowPartial` flag is deprecated and now a no-op.
